2

I think I'm already close to the solution but am missing the final step. I want to call a templated member function where I only have a pointer to the base class and the type is already predefined, so should not depend on the actual argument type.

This is what I have:

template <typename T> class TTemplateTest;

class CTemplateTest
{
public :
    CTemplateTest(){};
    template <typename T> double Process(T atValue)
    {
        return static_cast<TTemplateTest<T>* >(this)->Process(atValue);
    }
};
//------------------------------
template <class T>
class TTemplateTest:public CTemplateTest
{
    public :
        TTemplateTest() : CTemplateTest(){};
        virtual double Process(T atNewValue) {return atNewValue;};
};
//------------------------------
template <class T>
class TTemplateTestInt:public TTemplateTest<T>
{
public :
    TTemplateTestInt(){};
    virtual double Process(T atNewValue);
};
//------------------------------
template <class T> double TTemplateTestInt<T>::Process(T atNewValue)
{
    return atNewValue;
}

CTemplateTest* pTTest = new TTemplateTestInt<int>();

// application code
double d = 5.5;
double r;
r = pTTest->Process(d);

I would like to process the argument as integer in this example, no matter what type the argument is. It calls the right function but the value is garbage as the double is interpreted as integer instead of being converted.

I have looked at other questions (and other sites) but couldn't find a match or solution, e.g. calling a template function of a derived class

The solution probably is CRTP but I couldn't figure out how to use it. The application code should stay like this, the classes definitions can change. The reason for this code is that it is generated and used in runtime from some xml configuration file. So the type is not really known at the function call.

It would probably help if I could use the defined type, like:

template <typename T> double Process(T atValue)
{
    return static_cast<TTemplateTest<T>* >(this)->Process((this::type)atValue);
}

Or prevent the automatic creation of the function using double so the argument is converted to integer as it would happen on a non templated function.

Thanks for any help.

Edit: Next solution

Does this look valid? It doesn't need casting and we only need a handful of different basic types for the template so there won't be many redirector functions. And it should still be efficient (without typeinfo and such). I'm also posting it in case somebody else has a similar problem.

class CTemplateTest
{
public :
    CTemplateTest(){};
    virtual inline double Process(double adValue)=0;
    virtual inline double Process(int aiValue)=0;
};
//------------------------------
template <class T>
class TTemplateTest:public CTemplateTest
{
    public :
        TTemplateTest() : CTemplateTest(){};
        virtual inline double Process(double adValue) {
            return ProcessImp((T)adValue);
        }
        virtual inline double Process(int aiValue) {
            return ProcessImpl((T)aiValue);
        }
        virtual double ProcessImpl(T atNewValue)=0;
};
//------------------------------
template <class T>
class CTemplateTestInt:public TTemplateTest<T>
{
public :
    CTemplateTestInt(){};
    virtual double ProcessImpl(T atNewValue) {return atNewValue;};
};

This then gives the desired result with

CTemplateTest* pTTest = new TTemplateTestInt<int>();

// application code
double d = 5.5;
double r;
r = pTTest->Process(d);
// -> r = 5

Thanks

  • 2
    Your cast leads to UB in your case. – Jarod42 Jan 25 '18 at 14:26
  • Unsure what you want to do, what should not change... – Jarod42 Jan 25 '18 at 14:29
  • The application code. It should be possible to have a base class pointer and call it with various argument types but it should always call the predefined function. The cast is just what I came up with, I'm open to better solutions. – F. Cenedese Jan 25 '18 at 14:48
  • *"So the type is not really known at the function call."* C++ use wrong static typing, so type should be known at compilation... – Jarod42 Jan 25 '18 at 15:34
  • Seems to me pretty much the same problem as with why we don't have virtual template functions in C++ (actually, those would solve the problem...). – Aconcagua Jan 25 '18 at 16:04
  • 1
    @F.Cenedese The details about UB: The two template types within the function and the class are *independent*, and what happens in your template function is that you cast an object of type `TTemplateTestInt` (as which it was created) to the totally unrelated object type (apart from inheriting the same base class) `TTemplateTestInt` (deduced from `Progress(5.5);`) -- which is what the "weired" behaviour comes from... – Aconcagua Jan 25 '18 at 16:08
  • How many instantiations of `Progress` are we talking about? Is overloading the functions normally an option? – Aconcagua Jan 25 '18 at 16:12
  • To call a function in a derived class, you have to know its type if you want to cast to that type. Your cast would cast to a `TTemplateTest` but in reality you have a `TTemplateTest` which is **undefined behavior**. – Phil1970 Jan 25 '18 at 17:14
  • Also, usually you don’t mix virtual functions with templates in a design... – Phil1970 Jan 25 '18 at 17:18

2 Answers2

3

Your code looks fairly confusing, both in terms of naming and the logic. I don't know what you want to do, but I can explain why your code will lead to strange behavior, which also indicates there are some fondamental design flaw in your code.

To be simplified, your code has something similar to this:

class A {};

class B : public A {};

class C : public A {};

int main () {
    A *a_ptr = new B {};
    C *c_ptr = static_cast<C*>(a_ptr);
}

There is no reinterpret_cast, but this code still breaks the type system and will lead to undefined behavior. Because B has nothing to do with C, even if they both derive from A.

Back to your code, in class CTemplateTest, function

template <typename T> double Process(T atValue)
{
    return static_cast<TTemplateTest<T>* >(this)->Process(atValue);
}

will obtain type T by template argument deduction, not from any predefined type. Thus pTTest->Process(d) will deduce type T to be double and in that function, static_cast the this pointer to an irrelevant pointer TTemplateTest<double>*. But this pointer is indeed a TTemplateTest<int>*, these two classes have no relation except both deriving from CTemplateTest. So it's just the simplified case.

I don't know how to fix this code...

llllllllll
  • 16,169
  • 4
  • 31
  • 54
  • Yes, I have already found out that this doesn't work. How can I call a templated function where the type is taken from the instance/pointer instead of the argument? Or is this not possible at all? As I said the application code is the desired use case, the whole hierarchy and templates can be changed if there's even a way to reach this goal. – F. Cenedese Jan 25 '18 at 15:10
  • @F.Cenedese Without runtime type info, it's impossible. Because you want your function support both `int` and `double`, even without template, these 2 signatures have to be present in your base class. Compiler will select the one to be called only by *argument*. You can record some run time info of the type in your single base class and do dynamic dispatching by yourself. – llllllllll Jan 25 '18 at 15:50
  • Is there a possibility to get the type out of a templated class so the argument could be casted to this type before calling the function? – F. Cenedese Jan 25 '18 at 15:58
  • As I know, there is no direct way. But if only a few number of classes are involved, it's not difficult to write by yourself. And header `` might be helpful. – llllllllll Jan 25 '18 at 16:06
1

A few observations:

1) your base class has no virtual function, but your derived classes each have unique, unrelated virtual functions. They do not override anything so are kind of pointless being virtual.

2) The base class doesn't know what type the derived type was instantiated with. When CTemplateTest::Process is called, that T is the argument deduced for the function call, and is unrelated to the T used for the derived type. You're merely casting the object to the template instantiated with the type you were provided, ignoring the type your object actually is. This accounts for the garbage; it's undefined behavior.

3) There is no such thing as a template virtual function. One or the other; take your pick. That's basically what I think you are trying to simulate. You want the same function to take any type of parameter and have it passed, as that type, to the derived type, who knows how to process it its own templated way. I'm not sure of a satisfying way to accomplish this.

One thought is to pre-determine a fixed set of types you will accept and handle them individually, one virtual function per supported type. You may be able to make this a bit more flexible with a template parameter list and use the compiler to generate the functions, but I haven't thought that approach entirely through. :)

Chris Uzdavinis
  • 6,022
  • 9
  • 16