7

I have many functions that are very similar but run with different number and type of local objects:

template <class T> T* create1( const std::vector<std::string>& names )
{
    A a( names[0] );
    B b( names[1] );
    C c( names[2] );

    if ( a.valid() && b.valid() && c.valid() )
        return new T( a, b, c );
    else
        return NULL;
}

template <class T> T* create2( const std::vector<std::string>& names )
{
    D d( names[0] );
    E e( names[1] );

    if ( d.valid() && e.valid() )
        return new T( d, e );
    else
        return NULL;
}

create1<ABC>( { "nameA", "nameB", "nameC" } );
create2<DE>( { "nameD", "nameE" } );

Would variadic template help me achieve a refactoring of those functions as this?

template <class T, typename Args...> T* create()
{
    // loop over Args and create 2 or 3 objects
    // if (....valid())
    //    return T( ... );
    // else
    //    return NULL;
}

create<ABC,A,B,C>( { "nameA", "nameB", "nameC" } );
create<DE,D,E>( { "nameD", "nameE" } );

Checked How can I iterate over a packed variadic template argument list? and iterating over variadic template's type parameters with no success. Can't see how I could create a variable number of local objects of different kind...

Community
  • 1
  • 1
jpo38
  • 20,821
  • 10
  • 70
  • 151
  • Do they *need* to be local objects? Or are temporaries ok? – Rakete1111 Nov 18 '16 at 14:22
  • I'd prefer local if possible, because in fact their creation could fail and then lead to an early return of the function. Updated the post. – jpo38 Nov 18 '16 at 14:24
  • Why don't you check the size of the vector names and do a if statement ? You could do all this in only one function. This not the purpose of variadic template. Maybe you want to use [cstdarg](http://www.cplusplus.com/reference/cstdarg/) ? – Stargateur Nov 18 '16 at 14:39
  • 2
    what is wrong with `std::tuple`? – W.F. Nov 18 '16 at 15:13
  • @W.F.: Nothing, just did not think about it ;-) – jpo38 Nov 18 '16 at 16:20
  • @W.F. Just the order of initialization, if it matters to the OP. – bogdan Nov 18 '16 at 16:52

1 Answers1

11

First, don't take your names as a vector. Take them as a parameter pack. And, moreover, take them as a pack of what you actually want:

bool is_valid() { return true; }
template <class T, class... Ts>
bool is_valid(T& arg, Ts&... args) {
    return arg.valid() && is_valid(args...);
}

template <class T, class... Args>
T* create(Args&&... args) {
    if (is_valid(args...)) {
        return new T{args...};
    }
    else {
        return nullptr;
    }
}

And now just pass the right thing in:

create<T>(A{"nameA"}, B{"nameB"}, C{"nameC"});
create<T>(D{"nameD"}, E{"nameE"});

If, for some reason, you really want to separate the types and the names, you can do that too:

template <class T, class... Cs, class... As>
T* create_classes(As&&... args) {
    return create<T>(Cs{std::forward<As>(args)}...);
}
Danh
  • 5,916
  • 7
  • 30
  • 45
Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    Actually no, I probably simplified the example too much. See edited post, I need something more flexible. – jpo38 Nov 18 '16 at 14:26
  • Why are you taking two parameter packs to `create_classes`? – krzaq Nov 18 '16 at 14:58
  • @krzaq That's what OP wanted? – Barry Nov 18 '16 at 15:43
  • 1
    @Barry I'm sorry, I didn't phrase my question correctly. I was merely curious why you're relying on pretty bad-looking compiler error messages to tell the user that their variadic packs sizes mismatch. – krzaq Nov 18 '16 at 15:45
  • 3
    @krzaq gcc says "mismatched argument pack lengths while expanding..." and clang says "pack expansion contains parameter packs... that have different lengths". That's not any less clear than an explicit `static_assert()` would've been. – Barry Nov 18 '16 at 15:58