Robert Martin
Robert Martin

Reputation: 17177

C++ templates: can I call a function whose name is based on the input type name?

I have a codebase with a lot of enums associated functions that return singletons. Their names are formulaic:

enum Foo {...};
const MySingleton* Foo_Singleton();
enum Bar {...};
const MySingleton* Bar_Singleton();
...

Is it possible to write a C++ template that would choose the right getter function name without explicitly listing all the options?

template <typename EnumT>
inline const MySingleton* Get() {
  return EnumT ## _Singleton();
}

I know I'm using a nonsensical preprocessor directive here, but I hope it communicates my aim. I know I could write an overload for each enum, but there are a lot of them and it would be a pain to maintain, so I would prefer to avoid that solution.

Upvotes: 3

Views: 170

Answers (3)

W.F.
W.F.

Reputation: 13988

The easiest way would be to change your naming policy:

enum Foo {...};
const MySingleton* Get(Foo); // instead of const MySingleton* Foo_Singleton();
enum Bar {...};
const MySingleton* Get(Bar); // instead of const MySingleton* Bar_Singleton();

The mechanism involved is called tag dispatching. Now you could get appropriate singleton instance by simple:

Get(Foo{});

Upvotes: 4

jpo38
jpo38

Reputation: 21594

I doubt a solution mixing macros and template will be possible here.

A template-based solution that will work, but will require a wrapper function to be declared for every type.

First, define those functions:

const MySingleton* GetSingleton( Foo* var ) { return Foo_Singleton(); }
const MySingleton* GetSingleton( Bar* var ) { return Bar_Singleton(); }

Then just do:

template <typename EnumT>
inline const MySingleton* Get() {
  EnumT e;
  return GetSingleton( &e );
}

And then the appropriate GetSingleton will be picked up by the compiler, depending on EnumT.

Note, you can use a macro to declare the function, but I'm not sure it will be very useful:

#define DECLARE_WRAPPER(TYPE) const MySingleton* GetSingleton( TYPE* var ) { return TYPE##_Singleton(); }

DECLARE_WRAPPER( Foo )
DECLARE_WRAPPER( Bar )

Upvotes: 1

EmDroid
EmDroid

Reputation: 6046

One of the possibilities:

// template specification (to be specialized)
template <typename EnumT>
const MySingleton* Get();

#define JOIN_IMPL(a, b) a ## b
#define JOIN(a, b) JOIN_IMPL(a, b)

// template specialization for particular enum
#define DEFINE_GET_SINGLETON(EnumT)       \
template<>                                \
inline const MySingleton* Get<EnumT>() {  \
  return JOIN(EnumT, _Singleton)();       \
}

DEFINE_GET_SINGLETON(Foo)
DEFINE_GET_SINGLETON(Bar)

By using macros you can save some typing (at the expense of debugging - most debuggers cannot step through macros).

Upvotes: 2

Related Questions