1

TL;DR

What are the available tricks to pass undefined number of template classes to a macro in C++17 ? Example: my_macro(std::map<std::string,std::vector<double>>, double, std::deque<std::pair<bool,double>>)

With context

I'm in a context where I declare wrapper classes through macros. In these classes, I have a static function (written by a macro) to expose the wrapper in the API. Something like that:

wrapper_declare(Foo) // <- This is a macro declaring the template class, the
  public:                     //    inheritence, and some constructors
    void foo(double x);
    int bar() const;
    wrapper_expose(Foo, foo, bar); // <- This macro using __VA_ARGS__ defines a function 'expose'
};

It will be replaced by something like that:

template <>
class Wrapper<Foo> : public WrapperBase<Foo>
{
  private:
    typedef Wrapper<Foo> this_class;
    typedef WrapperBase<Foo> super;
  public:
    Wrapper() : WrapperBase<Foo>() { this->initEmpty(); }
    Wrapper(Wrapper<Foo> const&) = default;
    ~Wrapper() noexcept = default;
  public:
    void foo(double x);
    int bar() const;
    void expose()
    {
      magical_exposer<Foo>()
        .add("foo", &Wrapper<Foo>::foo)
        .add("bar", &Wrapper<Foo>::bar);
      // Yes, actually it's boost::python ;-)
    }
};

Note: I also have a generic wrapper for template types, where I can define classes like Wrapper<Hello<int,float,std::string>>. It will be a part of my problem below.

The expose function has also to expose dependencies on several other wrapped classes. Example:

template <>
class Wrapper<Foo> : public WrapperBase<Foo>
{
  private:
    typedef Wrapper<Foo> this_class;
    typedef WrapperBase<Foo> super;
  public:
    Wrapper() : WrapperBase<Foo>() { this->initEmpty(); }
    Wrapper(Wrapper<Foo> const&) = default;
    ~Wrapper() noexcept = default;
  public:
    void foo(double x);
    Wrapper<Bar> bar() const; // <- now return another wrapper
    void expose()
    {
      Wrapper<Bar>::expose(); // <- have to expose it before
      magical_exposer<Foo>()
        .add("foo", &Wrapper<Foo>::foo)
        .add("bar", &Wrapper<Foo>::bar);
      // Yes, actually it's boost::python ;-)
    }
};

With this approach, if I want to define this function with a macro, I have to pass the exposed types. Two problems with that:

  1. the number of dependencies is not fixed, and __VA_ARGS__ is already used by the macro for the functions to expose
  2. the exposed wrapper can contains comma, a problem with macros.

I imaginated a trick using a tuple type declared in the class and used by the expose function. Something like:

template <>
class Wrapper<Foo> : public WrapperBase<Foo>
{
  private:
    typedef Wrapper<Foo> this_class;
    typedef WrapperBase<Foo> super;
  public:
    Wrapper() : WrapperBase<Foo>() { this->initEmpty(); }
    Wrapper(Wrapper<Foo> const&) = default;
    ~Wrapper() noexcept = default;
  public:
    void foo(double x);
    Wrapper<Bar> bar() const;
    Wrapper<Hello<int,float,std::string>> hello() const;
    typedef std::tuple<Wrapper<Bar>,Wrapper<Hello<int,float,std::string>>> dependencies__;
    void expose()
    {
      dependencies__ d__;
      std::apply([](auto x){ decltype(x)::expose(); }, d__);
      magical_exposer<Foo>()
        .add("foo", &Wrapper<Foo>::foo)
        .add("bar", &Wrapper<Foo>::bar);
      // Yes, actually it's boost::python ;-)
    }
};

The typedef can be declared by the following macros

#define wrapper_dependencies(...) typedef std::tuple<__VA_ARGS__> dependencies__;
#define wrapper_no_dependencies() typedef std::tuple<> dependencies__;

But... I'm a little bit maniac. Most of my wrapped classes have no dependencies. Then I would like to have the call to wrapper_no_dependencies() as default, without having to write it. But it seems there is no way to overwrite a typedef.

Have you some ideas how to do that (maybe with another trick than the tuple) ?

Caduchon
  • 4,574
  • 4
  • 26
  • 67
  • Any specific reason you want preprocessor? This can probably be done with standard pack expansion and [fold expressions](https://en.cppreference.com/w/cpp/language/fold) – Mgetz Apr 18 '23 at 16:14
  • Because macros makes this kind of redundant work easy to write/read. I'm open to other solution, but readability is an objective. – Caduchon Apr 18 '23 at 17:41

1 Answers1

1

Not sure if it is acceptable, but define COMMA might help:

#define COMMA ,

// Test with simplified MACRO
#define DECL(TYPE, VAR) TYPE VAR;

DECL(std::map<int COMMA int>, a) // OK
// DECL(std::map<int, int>, b) // KO

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Ya, it works. But the goal is to have something easy to write. In my case, I have things like `Wrapper>,std::vector,Wrapper>,Value,Wrapper>` If I have to replace commas by a word, it becomes ugly ^^' – Caduchon Apr 18 '23 at 17:37