any alternatives to (lack of) virtual static functions?

C

carlm

Hello,
I searched for an answer to my question and found similar posts,
but none that quite addressed the issue I am trying to resolve.
Essentially, it seems like I need something like a virtual static
function (which I know is illegal), but, is there a way to provide
something similar? The class that is the target of my inquiry is a
template class that interfaces to one of several derived classes
through a pointer to a base class. The specific derived class that is
interfaced to depends on the template parameter provided. However, in
a few cases, I need to call a static (class) function associated with
the derived class when an objects of that class may not exist. Two
possibilities I have come up with are to 1) pass the static function
of the dervied class to the target class as a callback function or 2)
add the interface to the abstract base class, remove the static
designation of the function in the derived class, and use a half-
initialized object (ughh!) to access the function through a pointer to
the base class. Is there a better, more elegant way to do this?

Thanks,
Carl


Code example below.


template <typename T>
class Base
{
public:
// illegal to declare a virtual static function here
virtual void func1(T& a) = 0;
virtual void func2(T& b) = 0;
// ...



};


class Derived1 : public Base<int>
{
public:
static void Derived1Func(int& x); // type T is int in this case
void func1(int& a);
void func2(int& b);
// ...


};


class XyzType
{
XyzType() {}
~XyzType() {}


};


class Derived2 : public Base<XyzType>
{
public:
static void Derived2Func(XyzType& x); // type T is XyzType here
void func1(int& a);
void func2(int& b);
// ...


};


template <typename T>
class Target
{
void aFunc(Base<T> *ptr);
void bFunc(Base<T> *ptr);
// ...


};


template <typename T>
void Target<T>::aFunc(Base<T> *ptr)
{
T aT;

ptr->func1(T& aT); // great, works fine



};


template <typename T>
void Target<T>::bFunc(Base<T> *ptr)
{

// now here I need to get a handle on Derived1Func or Derived2Func
// ... or DerivedNFunc depending on the T parameter, or some other
// parameter if necessary. But I cannot call any of them directly
because
// this class primarily uses an interface to reference the specific
derived
// class (and does not know or care which derived class it is)


}

Thanks,
Carl
 
C

count0

carlm said:
[...]


template <typename T>
void Target<T>::bFunc(Base<T> *ptr)
{
// now here I need to get a handle on Derived1Func or Derived2Func
// ... or DerivedNFunc depending on the T parameter, or some other
// parameter if necessary. But I cannot call any of them directly
because
// this class primarily uses an interface to reference the specific
derived
// class (and does not know or care which derived class it is)
}

Specialize it:

template <>
void Target<int>::bFunc(Base<int> *ptr)
{
// ptr must be Derived1, in your specific case, cast it
Derived1 *p1 = (Derived1 *) ptr; // or use dynamic_cast...
p1->Derived1Func(...); // call the static function
}

template <>
void Target<XyzType>::bFunc(Base<XyzType> *ptr)
{
// ptr must be Derived2, cast it
Derived2 *p2 = (Derived2*) ptr;
p2->Derived2Func(...);
}
 
B

Ben Voigt [C++ MVP]

template said:
void Target<T>::bFunc(Base<T> *ptr)
{

// now here I need to get a handle on Derived1Func or Derived2Func
// ... or DerivedNFunc depending on the T parameter, or some other
// parameter if necessary. But I cannot call any of them directly
because
// this class primarily uses an interface to reference the specific
derived
// class (and does not know or care which derived class it is)

Your problem is that T doesn't uniquely determine the most derived class,
i.e. it is permissible to have:

class Derived1 : Base<int> {};
class Derived2 : Base<int> {}; // same T

If you add a template parameter for the true type, you can call its static
members.

template <typename T>
template <typename D>
void Target<T>::bFunc(D* dptr)
{
Base<T> *ptr = static_cast<Base<T>*>(dptr);

...

D::StaticFunction(); // function called varies with different D, unlike
generics in other languages
}

You can't have virtual dispatch without an object because virtual dispatch
depends on the runtime type of the object, no object... no runtime type.

Another option is to have a structure of function pointers, filled in for
each type. Then a pointer to this structure can be passed around, or a
virtual function in the interface can return it. This is essentially how
virtual calls work to begin with, except that the C++ virtual syntax doesn't
let you refer to the v-table and pass it around independent of an object.
 
B

Ben Voigt [C++ MVP]

Specialize it:

template <>
void Target<int>::bFunc(Base<int> *ptr)
{
// ptr must be Derived1, in your specific case, cast it
Derived1 *p1 = (Derived1 *) ptr; // or use dynamic_cast...
p1->Derived1Func(...); // call the static function

You don't (or shouldn't) call static functions using an object instance,
it's misleading at best.

Type::Member() is the correct way to refer to a static function.
 
D

David Wilkinson

Ben said:
You don't (or shouldn't) call static functions using an object instance,
it's misleading at best.

Type::Member() is the correct way to refer to a static function.

Ben:

ISTR that it is allowed by VC, but not by the C++ standard.
 
C

carlm

Ben Voigt said:
Your problem is that T doesn't uniquely determine the most derived class,
i.e. it is permissible to have:

class Derived1 : Base<int> {};
class Derived2 : Base<int> {}; // same T

Not following you here. Each of the derived classes inherits from the
abstract template class by providing a different type for the template
parameter and then overriding the corresponding pure virtual functions. This
seems right to me (and it compiles and runs as well).
If you add a template parameter for the true type, you can call its static
members.

template <typename T>
template <typename D>
void Target<T>::bFunc(D* dptr)
{
Base<T> *ptr = static_cast<Base<T>*>(dptr);

...

D::StaticFunction(); // function called varies with different D, unlike
generics in other languages
}

Yes, this seems helpful and more obvious than I realized. Here is what I tried
and it worked with my cooked up example:

template <typename T, typename D>
void Target<T,D>::aFunc(Base<T> *ptr)
{
T aT = 0;

ptr->func1(aT);
ptr->func2(aT);
D::StaticFunc(aT);
};


template <typename T, typename D>
void Target<T,D>::bFunc(Base<T> *ptr) // ptr is an out parameter here
{
T aT = 0;

D::StaticFunc(aT);
};


The only thing that I do not like about this solution is that there is no
general declaration for D:StaticFunc() here. It is sort of implicit by the
way it is called in the Target class.
You can't have virtual dispatch without an object because virtual dispatch
depends on the runtime type of the object, no object... no runtime type.

Yes, I understand that.
Another option is to have a structure of function pointers, filled in for
each type. Then a pointer to this structure can be passed around, or a
virtual function in the interface can return it. This is essentially how
virtual calls work to begin with, except that the C++ virtual syntax doesn't
let you refer to the v-table and pass it around independent of an object.

I like the solution proposed above much better.


Thanks,
Carl
 
C

carlm

Your problem is that T doesn't uniquely determine the most derived class,
Not following you here. Each of the derived classes inherits from the
abstract template class by providing a different type for the template
parameter and then overriding the corresponding pure virtual functions. This
seems right to me (and it compiles and runs as well).

Oh, I see what might have confused the issue. I made a mistake (at least
one!) in my example code. For Derived2, I should have provided:

class Derived2 : public Base<XyzType>
{
public:
static void Derived2Func(XyzType& x); // type T is XyzType here
void func1(XyzType& a);
void func2(XyzType& b);
// ...
};

I had ints in func1() and func2() previously. Sorry about that (the compiler
let me get away with it).
 
B

Ben Voigt [C++ MVP]

carlm said:
Not following you here. Each of the derived classes inherits from the
abstract template class by providing a different type for the template
parameter and then overriding the corresponding pure virtual functions.
This
seems right to me (and it compiles and runs as well).

Yes it does. But now knowing that T is int doesn't tell you that D is
Derived1...
Yes, this seems helpful and more obvious than I realized. Here is what I
tried
and it worked with my cooked up example:

template <typename T, typename D>
void Target<T,D>::aFunc(Base<T> *ptr)
{
T aT = 0;

ptr->func1(aT);
ptr->func2(aT);
D::StaticFunc(aT);
};


template <typename T, typename D>
void Target<T,D>::bFunc(Base<T> *ptr) // ptr is an out parameter here
{
T aT = 0;

D::StaticFunc(aT);
};


The only thing that I do not like about this solution is that there is no
general declaration for D:StaticFunc() here. It is sort of implicit by the
way it is called in the Target class.

C++0x will introduce "concepts" which place constraints on template type
arguments, including static members.
 
D

David Wilkinson

carlm said:
Oh, I see what might have confused the issue. I made a mistake (at least
one!) in my example code. For Derived2, I should have provided:

class Derived2 : public Base<XyzType>
{
public:
static void Derived2Func(XyzType& x); // type T is XyzType here
void func1(XyzType& a);
void func2(XyzType& b);
// ...
};

I had ints in func1() and func2() previously. Sorry about that (the compiler
let me get away with it).

carlm:

The compiler let you get away with it because you were just declaring
two non-virtual functions (taking an int parameter). This is allowed,
though not a good idea, and some compilers will warn you that you are
hiding the base class functions of the same names.
 
C

carlm

Yes it does. But now knowing that T is int doesn't tell you that D is
Derived1...

That's true and a good point. It seems, however, no matter how things are
arranged, either as your original proposal:

or as in my interpretation of it:
template <typename T, typename D>
void Target<T,D>::aFunc(Base<T> *ptr)
{
T aT = 0;

ptr->func1(aT);
ptr->func2(aT);
D::StaticFunc(aT);
};

two template parameters are needed--unless there is some additional template
magic that can be brought to bear on the issue. In any case, I believe I have
a decent solution now, at least one that is superior to passing in a callback
function. So, I will proceed with this.

Ben, David, "count0", thank you very much for your assistance on this matter.
 
B

Ben Voigt [C++ MVP]

carlm said:
That's true and a good point. It seems, however, no matter how things are
arranged, either as your original proposal:


or as in my interpretation of it:


two template parameters are needed--unless there is some additional
template
magic that can be brought to bear on the issue. In any case, I believe I
have
a decent solution now, at least one that is superior to passing in a
callback
function. So, I will proceed with this.

While T does not uniquely determine D, the converse is unique... you can
eliminate the redundancy thus:

template <typename T1>
class Base
{
public:
typedef T1 T;
};

template <typename D>
void aFunc(D *ptr)
// or aFunc(typename Base<D::T>* ptr)
{
typename D::T aT = 0;

ptr->func1(aT);
ptr->func2(aT);
D::StaticFunc(aT);
};
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top