Zack
Zack

Reputation: 6606

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>>

Upvotes: 4

Views: 397

Answers (3)

Daksh Gupta
Daksh Gupta

Reputation: 7804

if you're using C++11, you can make use of std::tuple to combine the variables into one.

A simpler example to understand the same can be

template <typename A, typename B, typename C>
void fn() {

 typedef std::tuple<A,B,C> myTuple;

  myTuple tpl;
  cout<<sizeof(std::get<0>(tpl))<<endl;;
  cout<<sizeof(std::get<1>(tpl))<<endl;
  cout<<sizeof(std::get<2>(tpl))<<endl;

}

int main() {

 fn<int,char,long>();

 return 0;
}

For the problem specific to your question, you can create vectors of tuple as

template <typename A, typename B, typename C>
void fn() {

    using mycomb = std::tuple<A,B,C>;

    vector<mycomb> v1;
    v1.push_back(make_tuple(10,'c',20.0));
}

In this way, you don't need to repeat the same. Tuple getter functions are bit awkward at first. The cout examples above demonstrates how to access tuple parameters

Hope this helps

Upvotes: 1

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.)

Upvotes: 5

Quentin
Quentin

Reputation: 63144

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

Upvotes: 4

Related Questions