5

I want to write an emacs function for debug purposes in c++ code. Basically I'm fed up typing the following:

std::cout << "variable is: " << variable << std::endl;

Instead I want to define an emacs function called say "td" for temp debug such that I do

M-x td RETURN
foo

And it will print

std::cout << "foo is: " << foo << std::endl;

I want it to behave like M-x goto-line whereby I type M-x goto-line and press enter then it prompts me for an input, in my case I want my input to be the variable name and something like td instead of goto-line.

Open to other suggestions, but I'd like to know how to code this type of function for future reference anyway.

Joey O
  • 135
  • 3
  • 2
    Try looking at the source to `goto-line` to see if you can figure it out. Use `C-h f goto-line RET` and click on the link that says `simple.el` to see the source. – nanny Nov 07 '14 at 16:43
  • 1
    Here are a couple of useful functions which you can combine to get the functionality you're after. See [`read-minibuffer`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Object-from-Minibuffer.html#Object-from-Minibuffer) and [`format`](https://www.gnu.org/software/emacs/manual/html_node/elisp/Formatting-Strings.html) in the manual. (And, for good measure here's the [intro to elisp](https://www.gnu.org/software/emacs/manual/html_node/eintr/) you can reference as well.) – Dan Nov 07 '14 at 16:47
  • 2
    Have you looked into [`yasnippet`](http://www.emacswiki.org/emacs/Yasnippet) – Ryan Nov 07 '14 at 18:52
  • 1
    The "expand into " concept is called abbrevs in Emacs, and might be worth looking in to. http://www.emacswiki.org/AbbrevMode – Thorbjørn Ravn Andersen Nov 08 '14 at 07:56
  • 1
    I'd do that kind of thing with a C++ macro. That way you can easily tell the temporary debug code (`DBG(foo)`) from the normal output, and disable the debug code in production. – Gilles 'SO- stop being evil' Nov 12 '14 at 04:57
  • +1 to @Dan's suggestion to read the **Intro to Elisp** manual. Help yourself. – Drew Nov 15 '14 at 04:19

2 Answers2

9

The interactive special form provides the easiest way to get input from a user.

(defun td (variable)
  (interactive "sVariable:")
  (insert (format "std::cout << \"%s is: \" << %s << std::endl;" variable variable)))

Here "sVariable:" consists of the "s" code character (read a string) and the prompt. (See Using interactive in the Emacs Lisp Manual for more.)

In addition to using a string with with code characters, the interactive special form can use a lisp form as its argument descriptor; this form should evaluate to a list of arguments.

This lets an interactive command compute its arguments from the context, record argument history, and so on.

read-from-minibuffer supports both setting the initial value and recording history; with its help we can make td remember what was entered and offer it upon next invocation.

(defun td (stream variable)
  (interactive
   (list
    (read-from-minibuffer "Stream: "
                          (when (boundp 'td-history) (car td-history))
                          nil nil 'td-history)
    (read-from-minibuffer "Variable: ")))
  (insert (format "%s << \"%s is: \" << %s << std::endl;" stream variable variable)))

PS: Since you are debugging C++ code, you can add __FILE__ and __LINE__ macros, to get something similar to

(defun td (variable)
  (interactive "sVariable:")
  (insert (format "std::cout << \"file \" << __FILE__ << \" line \" << __LINE__ << \" %s is: \" << %s << std::endl;" variable variable)))
Constantine
  • 9,072
  • 1
  • 34
  • 49
  • Thanks. That's great. Works really nicely. I modified it to also allow me to specify the stream, e.g. std::err or std::cout etc. So I have defun td(stream variable) etc. I would like to know if I can make stream default to the last one I used. In the same way that regexp-search does if you invoke it twice? – Joey O Nov 14 '14 at 14:46
  • This is how you should do it. But being C++, for maintainability, you'll probably want to format with an error macro (can't use a function of the FILE and LINE macros won't expand right). – Andy Nov 16 '14 at 09:03
  • `#define error(v) do { if (debug) std::cerr << "file " << __FILE__ << " line " << __LINE__ << " " #v " is: " << v << std::endl; } while(true)` – Andy Nov 16 '14 at 09:09
  • Dang it, past five minutes. I meant `while(0)` I really did. – Andy Nov 16 '14 at 09:17
5

Like this

(defun example(name)
  (interactive "sWhat's your name: ")
  (message "Hello, %s" name))

The key part is the "s" prefix. Use s for plain strings, b for buffers, etc.

rlazo
  • 983
  • 8
  • 13