2

I have a template builder/factory class that makes objects of type Foo, where the make() method is a template method, like so:

template<typename T1>
class FooMaker
{
  template<typename T2>
  Foo make(...) { ... }
};

The idea here is that T1 is bound to the builder since it's the same for all make() invocations, while T2 (which is always a function name) is usually different for each make() call.

I'm calling make() many times to create different types of objects, and the combination of a template class and a template functions means I have to use the template disambiguator before every call to make:

template <typename MAKER_T>
void make_some_foos()
{
    auto maker = FooMaker<MAKER_T>(...);
    Foo foo1 = maker.template make<Type1>(...);
    Foo foo2 = maker.template make<Type2>(...);
    // etc...
}

I'd like to needing the template keyword on each line that constructs a Foo above. In general I have many calls to make() on each FooMaker object, so a small amount of additional code when creating the factory would be fine.

Evidently I could do this using a macro which hides the template detail, but is there a good non-macro solution1?


1 I could move T1 and T2 to the same level - by making them both class template arguments, or both function template arguments, but the former would need a distinct maker for every new type T2 and the latter means redundantly specifying the same T1 in every make call.

Walter
  • 44,150
  • 20
  • 113
  • 196
BeeOnRope
  • 60,350
  • 16
  • 207
  • 386
  • If both `decltype(maker)` and `Type1` aren't dependent types, I believe you can just write `maker.make(...);`. I'm not sure on the exact rules for it, though. – Justin Feb 20 '18 at 21:58
  • @Justin `Type1` is always a function. What does it mean though for `decltype(maker)` to be dependent? Does it depend on the nature of `T1`? Sometimes `T1` is a plan (non-template class), and sometimes it is a template class (but I could perhaps change this). – BeeOnRope Feb 20 '18 at 22:01
  • 1
    you could make `make` a standalone function that takes `Maker` as a parameter like a `std::tuple` getter – W.F. Feb 20 '18 at 22:03
  • 1
    You can omit the `template` only if the type of `maker` is non-dependent, which means it doesn't directly or indirectly depend on any template parameters. (`Type1` doesn't matter for that.) Dependent types only exist inside template definitions. – aschepler Feb 20 '18 at 22:07
  • @aschepler - thanks, I updated the calling example to make it clearer that the creation of `FooMaker` is itself generally inside a template function where the function template parameter is used for `T1` in `FooMaker`. Do I understand you correctly that if the `T1` was simply some type like `int` (using a `FooMaker`) then it would be non-dependent? – BeeOnRope Feb 20 '18 at 22:13
  • 1
    @BeeOnRope Exactly. – aschepler Feb 20 '18 at 22:15

2 Answers2

6

This is one big reason std::get is a non-member function, rather than a member of std::tuple. Taking an idea from that pattern, you could create a non-member make_foo:

template <typename T2, typename T1, typename... Args>
Foo make_foo( FooMaker<T1>& maker, Args&& ... args ) {
    return maker.template make<T2>(std::forward<Args>(args)...);
}

And then just use it like:

auto maker = ...; // some code to construct the builder
Foo foo1 = make_foo<Type1>(maker, ...);
Foo foo2 = make_foo<Type2>(maker, ...);
// etc...
aschepler
  • 70,891
  • 9
  • 107
  • 161
2

You could ensure that T2 is deduced. To this end you need an argument that somehow provides the information. This could be a simple tag type

template<typename T>
struct tag { using type = T; };

template<typename T1>
class FooMaker
{
  template<typename T2>
  Foo make_internal(...) { ... }
public:
  template<typename Tag>
  Foo make(Tag, ...)
  { return make_internal<Tag::type>(...); }
};

auto maker = ...; // some code to construct the builder
Foo foo1 = maker.make(tag<Type1>{}, ...);
Foo foo2 = maker.make(tag<Type2>{}, ...);
// etc...

or an enum in combination with a traits type, or you can simply use a pointer to pass in the type:

template<typename T1>
class FooMaker
{
public:
  template<typename T2>
  Foo make(T2*, ...);
};

auto maker = ...; // some code to construct the builder
Foo foo1 = maker.make((Type1*)0, ...);
Foo foo2 = maker.make((Type2*)0, ...);
// etc...

though arguably, this is not very elegant.

Walter
  • 44,150
  • 20
  • 113
  • 196
  • Thanks! Can this be extended to the variant where rather than `template make(...)` I have `template make()` where `func_t` is a function typedef? – BeeOnRope Feb 20 '18 at 22:21
  • @BeeOnRope In that case, wouldn't you passing in the functor `F` (which could be a lambda) anyway? Perhaps you should make an example (or another post). – Walter Feb 20 '18 at 22:26
  • No, it's always a plain function, never a lambda or other stateful thing like `std::function` so I'm using the template function argument feature, and nothing is passed as arguments but the template argument is just "directly called", e.g., `typedef void (*func_t)(int); template call_f(/* no args! */) { F(42); }`. You are right though that it's different enough that I should perhaps create a new question. – BeeOnRope Feb 20 '18 at 23:24