3

Let's say I have some unspecified type called variant, as well as two functions allowing to convert to/from this type, with the following signature:

struct converter
{
  template<typename T>
  static variant to(const T&);

  template<typename T>
  static T from(const variant&);
};

Now, what I'd like to do is create wrappers for arbitrary C++ functions as in the following example:

SomeObject f_unwrapped(const std::string& s, int* x)
{
    //... do something with the inputs... 
    return SomeObject();
}

extern "C" variant f(variant s, variant x)
{
   return converter::to<SomeObject>(f_unwrapped(converter::from<std::string>(s), converter::from<int*>(x)));
}

Ideally I'd want the wrapper to be a one-line declaration or macro that would take only the f_unwrapped function and the name f as inputs.

I've tried to wrap the function into a function object, then do the bureaucratic work using variadic templates. While this does work, I don't know how to make the resulting function extern "C".

What is the most idiomatic way of achieving this goal?

Tartrate
  • 133
  • 2

1 Answers1

0

If we use the EVAL, helper, Conditional, and map macros from the first two code blocks here.

The map will need to be made more general for our needs.

#define MM1() MM_CALL1
#define MM_NEXT1(Macro,a,...)      \
  IS_DONE(a)(                       \
     EAT                             \
  ,                                   \
     OBSTRUCT(COMMA)() OBSTRUCT(MM1)() \
  )                                     \
  (Macro,a,__VA_ARGS__)
#define MM_CALL1(Macro,a,...)   \
  Macro(a)                       \
  MM_NEXT1(Macro,__VA_ARGS__)
#define MacroMap1(Macro,...) MM_CALL1(Macro,__VA_ARGS__,DONE)

#define MM2() MM_CALL2
#define MM_NEXT2(Macro,a,...)      \
  IS_DONE(a)(                       \
     EAT                             \
  ,                                   \
     OBSTRUCT(COMMA)() OBSTRUCT(MM2)() \
  )                                     \
  (Macro,a,__VA_ARGS__)
#define MM_CALL2(Macro,a,b,...)   \
  Macro(a,b)                       \
  MM_NEXT2(Macro,__VA_ARGS__)
#define MacroMap2(Macro,...) MM_CALL2(Macro,__VA_ARGS__,DONE)

We will also want the WithTypes and WithoutTypes from here.

We can define AMACRO to do the job you wanted.

#define AsVariant(param) variant param
#define ConvertFrom(type,param) converter::from<type>(param)
#define HEADDER(type,func,params) type func ##_unwrapped (WithTypes params)
#define WRAPPER(type,func,params) \
   extern "C" variant func (OBSTRUCT(MacroMap1)(AsVariant,WithoutTypes params)) \
   { \
   return converter::to< type >(func ## _unwrapped( \
         MacroMap2(ConvertFrom,IDENT params) \
      )); \
   }

#define AMACRO(type,func,params) \
  EVAL(                           \
    HEADDER(type,func,params);     \
    WRAPPER(type,func,params)       \
    HEADDER(type,func,params)        \
  )

Which will turn this:

AMACRO(SomeObject,f,(const std::string&, s, int*, x))
{
     // ... do something with the inputs ...
     return SomeObject();
}

Into this (after formatting):

SomeObject f_unwrapped (const std::string& s , int* x );

extern "C" variant f (variant s , variant x )
{
   return converter::to<SomeObject>(f_unwrapped(converter::from<const std::string&>(s),converter::from<int*>(x)));
}

SomeObject f_unwrapped (const std::string& s , int* x )
{
   return SomeObject();
}

NOTE: If the const needs removing from the parameter, a conditional, similar to the ISDONE, can be made and added to the ConvertFrom macro.

unDeadHerbs
  • 1,306
  • 1
  • 11
  • 19