3

The problem

I'm currently writing notes on C++ classes. Sometimes I annotate what I shouldn't do (because they cause compilation errors) so it would be really helpful if I could automatically insert compilation errors in the #+RESULTS section of source code blocks.

Consider the following example: I would like the following code block to produce

#+begin_src cpp
class A {
  static int n = 0;
};

int main() { return 0; }
#+end_src

this #+RESULTS code block

#+RESULTS:
#+begin_example
/tmp/babel-NYqDkg/C-src-HslBxN.cpp:11:14: error: ISO C++ forbids in-class initialization of non-const static member ‘A::n’
   11 |   static int n = 0;
      |              ^
#+end_example

Additional context

In sh source code blocks, I can accomplish a similar behavior by using the following header arguments

#+begin_src sh :prologue "exec 2>&1" :epilogue ":"
ls "this path doesn't exist"
#+end_src

#+RESULTS:
#+begin_example
ls: cannot access "this path doesn't exist": No such file or directory
#+end_example
2

Yes, you can do so by using the :post() header argument.

I will present two solutions. The first one is simpler but has two disadvantages. I would consider the second solution the way to go.

First solution

Consider the following Org Mode file. We can call the dash code block after the c++ code block is executed by executing org-babel-execute-src-block, which is executed by pressing C-c C-c when the element at point is a source code block.

* Compilation errors in C++

#+NAME: compile-source-cpp
#+begin_src dash :epilogue ":" :prologue "exec 2>&1"
g++ main.cpp && echo "Success"
#+end_src

#+begin_src cpp yes :main yes :tangle main.cpp :post compile-source-cpp()
int 12 = "a";
#+end_src

#+RESULTS:
#+begin_example
main.cpp: In function ‘int main()’:
main.cpp:2:5: error: expected unqualified-id before numeric constant
    2 | int 12 = "a";
      |     ^~
#+end_example

There are two disadvantages of this solution:

  1. The C++ source file would be executed twice: The first one because of C-c C-c is pressed and the second one because g++ is executed in the dash code block but at least the #+RESULTS of the corresponding block would show the compilation errors.

  2. Before pressing C-c C-c, you will need to ensure that org-babel-tangle is executed beforehand on the C++ code block so that the source file is created and g++ can then compile it.

Second solution

This solution gets rid of the two disadvantages of the first solution.

#+PROPERTY: header-args:cpp :tangle main.cpp :eval no

* Utilities

#+begin_src elisp
(defun org-babel-tangle-previous-src ()
  "Tangles the backward nearest source code block."
  (interactive)
  (save-excursion
    (goto-char
     (search-backward-regexp "^[[:space:]]*#\\+begin_src\\>"))
    (let ((current-prefix-arg '(4)))
      (call-interactively 'org-babel-tangle))))
#+end_src

#+NAME: org-babel-tangle-previous-src
#+begin_src elisp
(org-babel-tangle-previous-src)
#+end_src

#+NAME: compile-source-cpp
#+begin_src dash :prologue "exec 2>&1" :epilogue ":"
g++ main.cpp && echo "Success"
#+end_src

* Compilation errors in C++

#+begin_src cpp
int main
#+end_src

#+CALL: org-babel-tangle-previous-src() :post compile-source-cpp()

#+RESULTS:
#+begin_example
main.cpp: In function ‘int main()’:
main.cpp:3:1: error: expected initializer before ‘return’
    3 | return 0;
      | ^~~~~~
#+end_example

#+begin_src cpp
int number = "12 + 13";
#+end_src

#+CALL: org-babel-tangle-previous-src() :post compile-source-cpp()

#+RESULTS:
#+begin_example
main.cpp: In function ‘int main()’:
main.cpp:2:14: error: invalid conversion from ‘const char*’ to ‘int’ [-fpermissive]
    2 | int number = "12 + 13";
      |              ^~~~~~~~~
      |              |
      |              const char*
#+end_example

Pressing C-c C-c in the #+CALL statements will tangle the nearest source code block and then will execute g++ on the tangled file.

2
  • How do I get the results from solution 2 into a draw as shown in your example? I have tried setting :results on the call, as a header and in section properties header-args: :results output draw.
    – alls0rts
    Feb 20 at 17:10
  • Changing the defaults does work, ` (setq org-babel-default-header-args (cons '(:results . "replace drawer") (assq-delete-all :replace org-babel-default-header-args))) `
    – alls0rts
    Feb 20 at 17:37
1

If you look at ob-C.el function org-babel-C-execute, you'll see that it compiles and links a program to tmp-bin-file, then runs it and captures its output to results.

So you need a different source-file type eg cpp-wrong that you would execute using shell that invokes g++ and captures its stderr as you hinted above.

Maybe copy ob-C.el and try to modify it according ot the above blueprint?

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.