views:

127

answers:

4

**I've gotten a few suggestions to make my function pure generic, which would work, but I'd prefer limiting the function to only accept Base and its children.

Having trouble making a function that can accept arguments of a variadic template class base type, while the function will actually be called with classes that derive from Base. I've tried a few things. Here's the general idea. Given:

template<typename... Args> struct Base {
    std::tuple<Args...> data;
    ... //other stuff
};

struct DerivedA : Base<string, int> {
};

struct DerviedB : Base<bool, string, int> {
};

What's the correct way to create a function that does this:

string moosh_together(Base A, Base B) { //I only need access to Base's members
    return get<0>(A.data) + get<1>(B.data);
}

main() {
    DerivedA aThing;
        get<0>(aThing.data) = "foo";
    DerivedB bThing;
        get<1>(bThing.data) = "bar'd";
    cout << moosh_together(aThing, bThing) << endl;
}

Output:

foobar'd

I've tried a few variations of the moosh_together function, none of which work. Leaving it as above generates a compiler error about missing template arguments. I'm unsure how to pass through to the function the template arguments that define DerivedA and DerivedB.

Other things I've tried (shotgun method):

string moosh_together(Base<> A, Base<> B) {}
//err: conversion from 'DerivedA' to non-scalar type 'Base<>' requested

template<Base<typename... Args> T1, Base<typename... Args> T2>
string moosh_together(T1 A, T2 B) {}
//err: expected paramter pack before '...'

template<Base<Args...> T1, Base<Args...> T2>
string moosh_together(T1 A, T2 B) {}
//err: 'Args' was not declared in this scope
+1  A: 

Couldn't you create a base class in the inheritance hierarchy for Base and pass that to the moosh_together() function? (Low knowledge of c++ here)

Zachary Yates
Yes, but I need something that will handle a Base with members that are dependent on the template parameters passed to Base.
pheadbaq
So, if the base template has indefinite arity and you want to write a function which takes the template without type parameters but depends on the type parameters, how would you reference the types of the members of Base? In your definition of the problem they can't be defined at compile time.
Zachary Yates
Wow, that comment made my head hurt too.
Zachary Yates
@Zachary Yates: Lol, sorry for the headache :) I'm not against passing the template w/ types on to the function, just lacking a way to do it. In my mind, the compiler should know what flavors of Base to use, because the classes that derive from Base are explicitly defining what types are being used for the version of Base they inherit from.
pheadbaq
+2  A: 

When you write: string moosh_together(Base A, Base B), ask yourself what Base is. Base is a class template, not a class type.

In other words, given:

template <typename T>
struct foo {};

foo<int> and foo<float> are two totally different types, that just so happened to be made from the same class template. They have no common base class, and you cannot refer to them simply as foo anymore than you can refer to both int and float with a single type.

You could factor out the non-dependent parts of Base:

struct Core
{
    string name;
};

template <typename... Args>
struct Base : Core
{
    // ...
};

And then refer to the Core portions:

// pass by reference, to avoid unnecessary copying
string moosh_together(const Core& a, const Core& b);

Or just make the function totally generic:

template <typename BaseOne, typename BaseTwo>
string moosh_together(const BaseOne& a, const BaseTwo& b);

And say "if you have the necessary members, you can use this function".

GMan
Well, okay, factoring out the non-dependent members works in this specific example. Say DerivedA and DerivedB both depend on a member of Base that is itself dependent on the template parameters passed to Base? I'm really looking for a solution that can handle the dependent members. Sorry, I should have used a better example. I'll change the code.
pheadbaq
sbi corrected me on my "template class" / "class template" wording, which I now realize is what you were doing in the beginning of your answer. I had glazed over that portion and was just looking at factoring out non-dependent members. Thanks for the clarification.
pheadbaq
+4  A: 

Edit:

If you need both parameter packs, you can just put both in the template specification:

template<typename... ArgsA, typename... ArgsB>
string moosh_together(const Base<ArgsA...>& A, const Base<ArgsB...>& B) {
    return get<0>(A.data) + get<1>(B.data);
}

This works because the parameter packs are inferred from arguments and not specified in a list. Naturally you can't have a class that depends on multiple parameter packs.

Jon Purdy
The first one would require both `Base` instantiations to have the same template parameters, I think. I'm not sure how to get two variadic template parameters to allow two different bases.
GMan
@GMan: I am now, having tested it.
Jon Purdy
@Jon Purdy: See the code changes I made in my example code... I think they render your first solution undoable, but you and GMan have both mentioned pure generic...
pheadbaq
@pheadbaq: My latest edit works for the `std::tuple`-based question as well.
Jon Purdy
@Jon Purdy: Perfect! Whew, glad that one's over with. They need a "worshipped" or "we're not worthy!" badge. And thanks for the info on parameter pack inference, very good subtlety to know about.
pheadbaq
Ya know, now that I think about it, this is exactly what I tried to do with that second bit under "other things I've tried," except instead of specifying two different parameter packs as the template arguments, I stuffed two different parameter packs inside two Base's. So I guess this means in this particular scenario, the function argument is telling the compiler what to use as the function's template argument, which then gives the compiler the whole picture on the function arguments. Ugh... my head.
pheadbaq
A: 

I think the general expansion of this is

string moosh_together(Base<T1...> A1, Base<T2...> A2, ... Base<Tn...> An) {
    return get<0>(A1.data) + get<1>(A2) + ... + get<n-1>(An.data);
}

This could be written as follows

template<int I> 
string moosh_together() { return ""; }

template<int I, typename ...Base1Ty, typename ... Bases>
string moosh_together(Base<Base1Ty...> const& base1, Bases const&... bases) {
    return get<I>(base1.data) + moosh_together<I+1>(bases...); 
}

template<typename ... Bases>
string moosh_together(Bases const&... bases) {
    return moosh_together<0>(bases...);
}
Johannes Schaub - litb