views:

103

answers:

2

I've written a .cpp file with a number of functions in it, and now need to declare them in the header file. It occurred to me that I could grep the file for the class name, and get the declarations that way, and it would've worked well enough, too, had the complete function declaration before the definition -- return code, name, and parameters (but not function body) -- been on one line.

It seems to me that this is something that would be generally useful, and must've been solved a number of times. I am happy to edit the output and not worried about edge cases; anything that gives me results that are right 95% of the time would be great.

So, if, for example, my .cpp file had:

i2cstatus_t NXTI2CDevice::writeRegisters(
    uint8_t  start_register,    // start of the register range
    uint8_t  bytes_to_write,    // number of bytes to write
    uint8_t* buffer = 0)        // optional user-supplied buffer
{
...
}

and a number of other similar functions, getting this back:

i2cstatus_t NXTI2CDevice::writeRegisters(
        uint8_t  start_register,    // start of the register range
        uint8_t  bytes_to_write,    // number of bytes to write
        uint8_t* buffer = 0)

for inclusion in the header file, after a little editing, would be fine.

Getting this back:

i2cstatus_t writeRegisters(
        uint8_t  start_register,    
        uint8_t  bytes_to_write,    
        uint8_t* buffer);

or this:

i2cstatus_t writeRegisters(uint8_t  start_register, uint8_t  bytes_to_write, uint8_t* buffer);

would be even better.

+4  A: 

I think you got it backwards. The declaration is the crucial piece here. The implementation is the details. You are trying to save yourself a bit of typing at the expense of not carefully designing and crafting the interface, which is very important in any language, but in C++ it's the thing.

Start from the header files. Then, if you like, generate stub implementation bodies and maybe test case wrappers, then start filling in the functions. Build, test, fix, expand, leather, repeat.

Edit:

A rant that doesn't fit into a comment:

My question then would be - if it's not "public interface", why do you need it in a header? My strong opinion is that everything a header file contains is the interface, no matter how small and what internal/external subsystem is exposing it to what other part. It doesn't seem significant for one guy project. But sooner or later a second pair of eyes (probably even your own, three or six months down the road) will have to go through that interface again and figure out why it was made public, what the author was thinking, what the purpose is, etc. etc.

Trivial? Yes! But I can't tell you how many times I cursed the original author (including myself) of some code I had to read and understand (again), and this trivial rule of thumb would have saved me so much hair :)

Edit 2:

... how do I declare a private member function without declare it with the rest of the class, in the header?

This is known failure of C++ itself. See, for example, Sutter's (Mostly) Private article. There are several ways of dealing with this at the design level:

  • In C++ not everything has to be a class, free functions are great. Declare and use a function pointer, implement bunch of matching functions in the .cpp file, put pointers into a table or an STL container.
  • Forward declare a class, so it's opaque to the user, amend the interface to operate on reference or a pointer.
  • Use a pimpl idiom (also here)
Nikolai N Fetissov
Generally, I agree with your sentiment. But... 1) it doesn't really matter which you write first, as long as your interface is designed first and 2) C++ has the unfortunate property of putting implementation details in the header files. I usually don't write my private declarations before their implementations. Either way, this answer doesn't help the OP to achieve either approach :)
Stephen
It doesn't. The point was not to do either. Focus on the public interface.
Nikolai N Fetissov
There are times when it is obvious that you need a given function -- and not necessarily for the public interface. For example, if you are writing a CPU emulator, with a function for each op-code, then you've got a large number of simple functions that really don't need much in the way of design beforehand. In such a case, why not code them up and then extract the function names for the header?
Clinton Blackmore
Then you use polymorphism, because writing a function for each possible opcode would be suicide for extendability.
DeadMG
Exactly. Or write a script to generate a table of pointers to these simple functions *in the implementation file*.
Nikolai N Fetissov
Pertaining to "if it is not public interface, why do you need it in the header", how do I declare a private member function without declare it with the rest of the class, in the header?
Clinton Blackmore
@DeadMG It was five years ago when I wrote an 80186 emulator. I suppose it was every operation that had its own function, not every op-code. (Another part of the code took the op-code, figured out the operation and determined which registers/memory addresses were involved and then called the function.) All the different operations still need to be written at some point.
Clinton Blackmore
Thanks for the notes in Edit 2. That is very interesting.
Clinton Blackmore
+1  A: 

I compiled exuberant ctags version 5.8. This command gets me what I want:

/usr/local/bin/ctags -x --c-kinds=f $SOURCE |
awk -v OFS=" " '$1=$1' |
cut -d " " -f 5- |
sed -e 's/[A-Za-z]*:://g' |
sed -e 's/)$/);/'

where you substitute the filename you are interested in in place of $SOURCE.

Note that the ctags command by itself gives a reasonable output, and you can throw in a -u flag if you want the output in the order it appears in the file, instead of in alphabetical order.

Clinton Blackmore
Your last 'sed' line might run into problems if the function contained a pointer to function, amongst other possible uses of parentheses within a single function. Consider: `void (*signal(int sig, void (*func)(int)))(int);`.
Jonathan Leffler
I think, if you find you need to use this often, you're probably doing it wrong... but +1 for figuring out the solution and posting it back :) (And currently, the highest voted answer doesn't help at all)
Stephen
@Jonathan Leffler - Thanks for the edit, it is much clearer. I had actually considered that, with a global replacement, putting in the semicolon could cause a problem. I figured it would work most of the time, though.
Clinton Blackmore
@Stephen - Thank you for coming back, and for the kind words. I do agree with you -- typically one wants to design the public interface first.
Clinton Blackmore
Under what circumstances would `sed -e 's/)$/);/'` give the wrong result? Also, of course, the two sed invocations could be combined, but that isn't a big deal in this application.
Jonathan Leffler
@Jonathan - Good call. I can't see any circumstance where it'd fail. I'll update the post.
Clinton Blackmore
Note that functions that take a lot of parameters (and would not fit within one screen width) only shows the opening parenthesis and no parameters. : (
Clinton Blackmore