tags:

views:

91

answers:

5

I have a superclass which is defined in terms of a few internal types it uses. Subclassing is performed as so:

template <class InternalType1, class InternalType2>
class Super 
{
    ...
}

class Sub : Super <interalTypeClass1, interalTypeClass2>
{ 
    ...
}

But when I want to write a function that takes a pointer to the superclass, this happens :

template <class InternalType1, class InternalType2>
void function(Super<InternalType1, InternalType2>* in) { ... }

The user really shouldn't know anything about the inside classes, and should really just concern himself with the use of the function. Some of these template lists become very very large, and expecting the user to pass them every time is wasteful, in my opinion.

Any suggestions?

EDIT: The function needs to know the internal types in use, so unless there is a way to access template types at compile time, I think there is no solution?

Potential solution: Have each class do the following:

#define SubTemplateArgs <SubTypeName, SubInternalType1, SubInternalType2>

?

A: 

add the following typedef to the Sub class:

typedef Super<internalTypeClass1,internalTypeClass2> MySuper;

Now you can write

void function (Sub::MySuper *in);
Patrick
+1, Unfortunately, typedefs don't work for partial specializations :(
Stephen
i would add this as a free typedef, not as a member typedef, because the OP will have more than 1 Sub type, and then there exist `Sub1::MySuper, Sub2::MySuper` ...and noone will know if these are identical... so rather use a free typedefs... then you can also write the Subclasses as class Sub : MySuper {/*..*/};
smerlin
Well the thing is that the function needs to know the internal types. Is there any way that I can pass them somehow?
Alex
@Alex: add public typedefs to Super: `typedef InternalType1 internal1_type;` and `typedef InternalType2 internal2_type;`, then you can access these types in your function using `Super::internal1_type`
smerlin
I like it. I've added another solution that occurred to me in an edit to my original post.
Alex
I think i'm missing something. Wouldn't that make `function` only accept `Super<internalTypeClass1, internalTypeClass2>*` ? But the questioner seems to want also accept `Super<int, int>*`.
Johannes Schaub - litb
A: 

The problem with typedefs is that compilers use the full class name in error messages. Maybe something like this will work better:

class MySuper : public Super<internal1, internal2> {
    // forward constructors
}
Amnon
That's a good point. I think this is something I will do from now on, because that infuriates me when the standard library does it.
Alex
thats a problem with the compiler, not with the language the fact remains that a typedef is the best solution from a language side of view, but you are right, sometimes the full class name may be irritating, but in other cases you wont be able to debug compile errors in a fast way if you havent got (or until you have...) all the information about the real type of a typedef.
smerlin
+1  A: 

You can change the Super interface

template <class Types>
class Super {
    ...
};

class SubTypes {
  typedef interalTypeClass1 InternalType1;
  typedef interalTypeClass2 InternalType2;
};

class Sub : public Super <SubTypes> { 
    ...
};

Then your function template looks more user-friendly

template <class Types2>
void function(Super<Types>* in) { 
    ... 
}

Sub s;
// ...
function(&s); 

But the user doesn't need to care about those arguments - the compiler figures out the template arguments itself if the user passes a proper function argument as in the example. Think of std::operator<< and std::string. The latter really looks like the following, with T being char

std::basic_string< T, std::char_traits<T>, std::allocator<T> >

And the operator<< printing it to an output stream really accepts such a type, still C++ coders don't need to care about allocators or character traits. Proper documentation is there to teach them to use std::string easily and just pass it around and print it out.

Johannes Schaub - litb
+2  A: 
template <class TInternal1, TInternal2>
class Super {
    private:
        /*...*/
    public:
        typedef TInternal1 internal_type_1;
        typedef TInternal2 internal_type_2;
        /*...*/
};

typedef Super<int, char>    IntCharSuper;
typedef Super<bool, char>   BoolCharSuper;

class Sub1 : IntCharSuper {/*...*/};
class Sub2 : IntCharSuper {/*...*/};
class Sub3 : BoolCharSuper {/*...*/};


/***
 * functionA (below) can only take a pointer to an object of a
 * class which inherits from IntCharSuper
 **/
void functionA(IntCharSuper* in){
    IntCharSuper::internal_type_1 internal1_variable;
    IntCharSuper::internal_type_2 internal2_variable;
    // we now have 2 variables of the internal types...
}

/***
 * functionB (below) can take a pointer to an object of a class which inherits from any kind of Super, 
 * no matter what the template parameters of that Super are.
 * so functionB will work for types which inherit from 
 *    IntCharSuper, BoolCharSuper, Super<YourCustomType, void*>, or anything else for TInternal1 and TInternal2.
 **/
template <class TSuper>
void functionB(TSuper* in) {
    typename TSuper::internal_type_1 internal1_variable; /* typename is needed here */
    typename TSuper::internal_type_2 internal2_variable; /* typename is needed here */
    // we now have 2 variables of the internal types...
}

int main(int argc, const char* argv) {
        Sub1 s1;
        Sub2 s2;
        Sub3 s3;
        functionA(&s1);  //OK, s1 inherits IntCharSuper
        functionA(&s2);  //OK, s2 inherits IntCharSuper
        functionA(&s3);  //ERROR, s3 hasnt got IntCharSuper as parent

        functionB(&s2); //OK, s2 inherits from a class which defines internal_type_1 and internal_type_2
        functionB(&s3); //OK, s3 inherits from a class which defines internal_type_1 and internal_type_2

        return 0;
}
smerlin
+1  A: 

The user really shouldn't know anything about the inside classes, and should really just concern himself with the use of the function.

The problem is that each different combination of template options is defining a different class.

You can't do the following

vector<Super<int,int>> a;
a.push_back( Super<float,float>() );

Similarly the function you don't want to write has the same issue, you have multiple overloads of the function.

You can choose to name the various combinations (as already suggested) using typedef, or perhaps you could use some more common parent class that Super is based on.

class SuperDoooper {
  virtual SomeMethod();
}
template <class InternalType1, class InternalType2>
class Super 
{
    SomeMethod() { InternalType1.. }
}

class Sub : Super <interalTypeClass1, interalTypeClass2>
{ 
    ...
}


function( SupoerDoooper *in ) { in->SomeMethod(); }
Greg Domjan