Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is it possible to "bundle" template parameters in C++?

Is there a way to "bundle" template parameters together to avoid repetition?

I have several classes and functions that all use the same three template parameters. It is not unusual to have a function that uses each class/function once. The resulting code gets very messy very quickly. Is there a more concise way to write this code?

// ContextFactory is a pointer to functions that instantiate objects that are subtypes of MetricContext
template<typename VertexID, typename EdgeMembershipType, typename SetBitmap>
using ContextFactory = MetricContext <VertexID, EdgeMembershipType, SetBitmap> *(*)(const char *);

template<typename VertexID, typename EdgeMembershipType, typename SetBitmap>
    static vector<ContextFactory<VertexID, EdgeMembershipType, SetBitmap>> buildCFList() {
      vector<ContextFactory<VertexID, EdgeMembershipType, SetBitmap>> answer;
      answer.push_back(MetricContext<VertexID, EdgeMembershipType, SetBitmap>::template make<NeoContext<VertexID, EdgeMembershipType, SetBitmap >>);
      return answer;
    };

Notice that almost half of this function is repetition of the string <VertexID, EdgeMembershipType, SetBitmap>>, but each use of this string applies to a different class or function, so I don't think alias will work.

(If it helps, the purpose of this function is to create an array of pointers to functions that will create objects that are subtypes of MetricContext<VertexID, EdgeMembershipType, SetBitmap>>

like image 904
Zack Avatar asked Sep 06 '25 03:09

Zack


2 Answers

A rather more specific approach than @Quentin's is to make your template depend on a single parameter - which is expected to have typedefs for VertexID, EdgeMembershipType, and SetBitmap.

// ContextFactory is a pointer to functions that instantiate objects that are 
// subtypes of MetricContext
template<typename Types>
using ContextFactory = MetricContext <Types> *(*)(const char *);

template<typename Types>
    static vector<ContextFactory<Types>> buildCFList() {
      vector<ContextFactory<Types>> answer;
      answer.push_back(MetricContext<Types>::template make<NeoContext<Types>>);
      return answer;
    };

Note that when you want to actually use one of the typedefs, you will need to use for example: typename Types::VertexID.

(Ideally you would come up with a better name than Types for the template argument.)

like image 176
Martin Bonner supports Monica Avatar answered Sep 08 '25 12:09

Martin Bonner supports Monica


Yes, this is possible. Let's define a little helper class to hold a list of types:

template <class... > struct pack { };

And a metafunction that instantiates a template with what's inside a pack:

template <template <class... > class T, class P>
struct unpack_;

template <template <class... > class T, class... P>
struct unpack_<T, pack<P...>> {
    using type = T<P...>;
};

template <template <class... > class T, class P>
using unpack = typename unpack_<T, P>::type;

And we can now store and use our parameter pack:

template <class A, class B, class C>
struct Foo { };

using Params = pack<int, float, double>;

unpack<Foo, Params> f; // f is a Foo<int, float, double>

See it live on Coliru

like image 45
Quentin Avatar answered Sep 08 '25 11:09

Quentin