views:

183

answers:

6

So I have a series of global functions, say:

foo_f1(int a, int b, char *c);
foo_f2(int a);
foo_f3(char *a);

I want to make a C++ wrapper around these, something like:

MyFoo::f1(int a, int b, char* c); 
MyFoo::f2(int a);
MyFoo::f3(char* a);

There's about 40 functions like this, 35 of them I just want to pass through to the global function, the other 5 I want to do something different with.

Ideally the implementation of MyFoo.cpp would be something like:

PASSTHRU( f1, (int a, int b, char *c) );
PASSTHRU( f2, (int a) );

MyFoo::f3(char *a)
{
   //do my own thing here
}

But I'm having trouble figuring out an elegant way to make the above PASSTHRU macro.

What I really need is something like the mythical X getArgs() below:

MyFoo::f1(int a, int b, char *c)
{
  X args = getArgs();
  args++; //skip past implicit this..
  ::f1(args);  //pass args to global function 
}

But short of dropping into assembly I can't find a good implementation of getArgs().

A: 

My initial thought, and this probably won't work or others would have stated this, is to put all your base functions together in a class as virtual. Then, write the functionality improvements into inherited classes and run with it. It's not a macro wrapper, but you could always call the global functions in the virtual classes.

With some assembly trickery, you could probably do exactly what you'd want, but you would lose portability more than likely. Interesting question and I want to hear other's answers as well.

Michael Dorgan
A: 

You may want to use a namespace if you want to not deal with class stuff, like this. You could also use static member methods in a class, but I think that people don't like that anymore.

#ifndef __cplusplus
#define PASSTHRU(type, prefix, func, args)  type prefix##_##func args
#else
#define PASSTHRU(type, prefix, func, args)  type prefix::func args
#endif

Or

#ifndef __cplusplus
#define PASSTHRU(type, prefix, func, ...)  type prefix##_##func(__VA_ARGS__)
...
nategoose
+6  A: 

You could use Boost.Preprocessor to let the following:

struct X {
    PASSTHRU(foo, void, (int)(char))
};

... expand to:

struct X {
    void foo ( int arg0 , char arg1 ) { return ::foo ( arg0 , arg1 ); }
};

... using these macros:

#define DO_MAKE_ARGS(r, data, i, type) \
  BOOST_PP_COMMA_IF(i) type arg##i

#define PASSTHRU(name, ret, args) \
  ret name ( \
    BOOST_PP_SEQ_FOR_EACH_I(DO_MAKE_ARGS, _, args) \
  ) { \
    return ::name ( \
      BOOST_PP_ENUM_PARAMS(BOOST_PP_SEQ_SIZE(args), arg) \
    ); \
  }
Georg Fritzsche
damn, beat me to it; more elegant too. Yours has a bug though ;)~
Noah Roberts
This leads to infinite recursion. :)
GMan
@GMan: Oops, thanks.
Georg Fritzsche
I would consider using a 3 arguments macros: name, return type, arguments type. It's not that obvious in your macro where the return type goes and this leads to an unnecessary splits afterward.
Matthieu M.
@Matthieu: fair enough, changing that.
Georg Fritzsche
+2  A: 

Slightly different syntax but...

#include <boost/preprocessor.hpp>
#include <iostream>

void f1(int x, int y, char* z) { std::cout << "::f1(int,int,char*)\n"; }

#define GENERATE_ARG(z,n,unused) BOOST_PP_CAT(arg,n)
#define GET_ARGS(n)  BOOST_PP_ENUM(n, GENERATE_ARG, ~)

#define GENERATE_PARAM(z,n,seq) BOOST_PP_SEQ_ELEM(n,seq) GENERATE_ARG(z,n,~)

#define GENERATE_PARAMS(seq) BOOST_PP_ENUM( BOOST_PP_SEQ_SIZE(seq), GENERATE_PARAM, seq )

#define PASSTHROUGH(Classname, Function, ArgTypeSeq) \
  void Classname::Function( GENERATE_PARAMS(ArgTypeSeq) ) \
{ \
  ::Function( GET_ARGS( BOOST_PP_SEQ_SIZE(ArgTypeSeq) ) ); \
}

struct test
{
  void f1(int,int,char*);
};

PASSTHROUGH(test,f1,(int)(int)(char*))

int main()
{
  test().f1(5,5,0);

  std::cin.get();
}

You could get something closer to yours if you use tuples, but you'd have to supply the arg count to the base function (you can't derive a size from a tuple). Sort of like so:

PASSTHROUGH(test,f1,3,(int,int,char*))

That about what you're looking for? I knew it could be done; took about a half hour to solve. You seem to expect that there's an implicit 'this' that has to be gotten rid of but I don't see why...so maybe I misunderstand the problem. At any rate, this will let you quickly make default "passthrough" member functions that defer to some global function. You'll need a DECPASSTHROUGH for the class declaration if you want to skip having to declare them all...or you could modify this to make inline functions.

Hint: Use BOOST_PP_STRINGIZE((XX)) to test the output of preprocessor metafunctions.

Noah Roberts
The 'this' would only be an issue if you were accessing the memory directly. Thanks for the solution, hopefully people here will be ok with including the Boost preprocessor (we're not using Boost, but the PP looks standalone).
Ryan
Well, if the people there aren't ok with using boost you could just do what every other C++ dev does when they can't use boost: attempt to write those parts that you really need, do it badly, and lament at the incompetence of your bosses.
Noah Roberts
@Ryan: PP is standalone and even works in C. Alternatively you can always extract Boost components with the [bcp](http://www.boost.org/tools/bcp/index.html) tool.
Georg Fritzsche
A: 

Perfect forwarding relies on rvalue references. STL has a blog entry on it at http://blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx and you would want to choose a compiler that supported the feature to take this approach. He's discussing Visual C++ 2010.

Kate Gregory
Noah Roberts
+2  A: 

At 40-odd functions, you could type the wrappers out by hand in an hour. The compiler will check the correctness of the result. Assume an extra 2 minutes for each new function that needs wrapping, and an extra 1 minute for a change in signature.

As specified, and with no mention of frequent updates or changes, it doesn't sound like this problem requires a cunning solution.

So, my recommendation is to keep it simple: do it by hand. Copy prototypes into source file, then use keyboard macros (emacs/Visual Studio/vim) to fix things up, and/or multiple passes of search and replace, generating one set of definitions and one set of declarations. Cut declarations, paste into header. Fill in definitions for the non-passing-through functions. This won't win you any awards, but it'll be over soon enough.

No extra dependencies, no new build tools, works well with code browsing/tags/intellisense/etc., works well with any debugger, and no specialized syntax/modern features/templates/etc., so anybody can understand the result. (It's true that nobody will be impressed -- but it will be the good kind of unimpressed.)

brone