I’ve been doing more C++ than usual for courses, so I ended up revisiting my C++ configuration and loading up some old customizations that I had in order to be more productive. I didn’t pull in all of my old ones, such as all the CEDET stuff that had been slowing down my emacs significantly, but I did find some useful customizations that I had and wanted to put back in.
The first was to define a project type for C/C++ projects using eproject. This lets you easily switch between files in the same project, as well as perform other actions described in the linked post. I define the project base to just be the topmost directory with a Makefile.
(define-project-type cpp (generic) (look-for "Makefile") : relevant-files ("\\.cpp" "\\.c" "\\.hpp" "\\.h"))
Another re-addition is member-function.el, a mode that will automatically expand member functions in class definitions with stubs. This ends up saving me a lot of time, since I don’t have to write out the function definitions twice for every function - I just add it to the class definition and then it appears in the corresponding implementation file.
Normally expand-member-function must be run interactively and prompt you for the header and implementation file, but I just created a function that would find these values and run it with the correct values. I added it to c-mode-hook, which will cause it to be run whenever a C/C++ file is opened, without me having to do anything.
I also got tired of typing in #ifndef blocks for my header files, so I wrote a function that would insert this in newly-created .h and .hpp files. I also added this function to c-mode-hook, making it so I never have to type one again (I hope).
(defun h-file-create () "Create a new h file. Insert a infdef/define/endif block" (interactive) (if (or (equal (substring (buffer-name (current-buffer)) -2 ) ".h") (equal (substring (buffer-name (current-buffer)) -4 ) ".hpp")) (if (equal "" (buffer-string)) (insert "#ifndef "(upcase (substring (buffer-name (current-buffer)) 0 -2)) "_H\n#define " (upcase (substring (buffer-name (current-buffer)) 0 -2)) "_H\n\n#endif")))) (defun c-file-enter () "Expands all member functions in the corresponding .h file" (interactive) (let ((c-file (buffer-name)) (h-file (concat (substring (buffer-name (current-buffer)) 0 -3 ) "h"))) (if (equal (substring (buffer-name (current-buffer)) -4 ) ".cpp") (if (file-exists-p h-file) (expand-member-functions h-file c-file)))))
I also occasionally write small C++ programs for testing purposes. In these cases, it’s nice to be able to do a M-x compile and have it work without having to manually type in a compile-command. yourself. This will set the compile-command to one that will appropriately compile the current file if no Makefile exists whenever a c/c++ file is opened.
(add-hook 'c-mode-hook (lambda () (unless (file-exists-p "Makefile") (set (make-local-variable 'compile-command) (let ((file (file-name-nondirectory buffer-file-name))) (format "%s -c -o %s.o %s %s %s" (or (getenv "CC") "g++") (file-name-sans-extension file) (or (getenv "CPPFLAGS") "-DDEBUG=9") (or (getenv "CFLAGS") "-ansi -pedantic -Wall -g") file))))))
I also finally learned how to properly use tags. Tags allow you to quickly navigate to declarations of functions and symbols in your program. To use tags, you must first create a tags file using either etags or exuberant ctags - etags comes with emacs, so you will have at least one of these. You use one of these to generate a TAGS file just by using ‘etags’ in the directory you wish to create a TAGS file in. It will automatically scan all files and subdirectories to create this file.
Once your index is created, you can jump to the definition of a function with M-.. This will prompt for a TAGS file the first time you use it, and then use that index to jump to the function definition you supply (defaulting to the one at point). If there are several conflicting definitions, C-u M-. will go to the next possible definition. Once you are done with that location, M-* will take you back to where you originally jumped from. C-x 4 . will perform a find-tag, but will open the declaration in another window. There are a few other tags commands, but these are the most useful ones. To find out more, read the TAGS node in the Emacs manual.
One last utility I added was . Cscope is the reverse of etags; given a symbol, it will find all uses of that symbol. To use it, cscope must be installed; in Ubuntu you can do this with ’sudo apt-get install cscope’. To use it, just use M-x cscope-find-c-symbol to have it output a list of all references to the symbol; this list is linked to your source, so you can jump to files by pressing ‘enter’ on the line you have selected. cscope-find-functions-calling is also useful; it iwll return a list of all functions that call the function you specify. There are a few more commands, but these are the ones I use; look at the commands starting with cscope- to find a list of them. To enable cscope, you need to put cscope.el in your load path and add the following to your initialization:
(require 'cscope)
Tags: emacs
Nice post. Pretty thorough and full of useful stuff. Thanks.
Great post. You have run into the same annoying quirks which I have.
I must admit member-function.el reimplements a lot of parsing thats done by cc-mode anyway. I suspect a much more generic implementation could be written as a cc-mode add on..
On the other hand, no other language has the header/body file split, so it’s a moot point.
Hi, I copied the compile hook to my init file but it doesnt take. I’m using carbonemacs. Any ideas?
What error is it giving you?
nflath, none, just that M-x compile would still look for the Makefile. I changed c-mode-hook to c++-mode and it worked.
err, I changed c-mode-hook to c++-mode-hook and it worked.