Here is a problem involving inheritance, template classes and function pointers. I'm developing a modular tool, here is a minimal depiction of it.
I have a generic base class that I need to be as general as possible (for reasons unspecified here):
// base class
class Base {
public:
virtual void assess( ) = 0;
};
Then I have a more specialized derived class, that I define as a template:
// derived template class
template <typename T>
class Derived : public Base
{
protected:
T mValue;
public:
void assess( );
T value( ) { return mValue; }
};
// function specialization here
template<>
inline void Derived<int>::assess( )
{
mValue = 3;
}
Some parts are specialized, like the assess() function, as shown above.
Then I have an extra layer of inheritance. The main idea is to have a general assess() function that involves a function pointer with a Base object as argument.
// class specialization for special evaluation through function pointers
template <typename T>
class DerivedFuncPtr : public Derived<T> {
protected:
T (*mFuncPtr)( Base& );
Base *mFuncVar;
public:
DerivedFuncPtr<T>( T (*f)(Base&), Base& variable )
{
mFuncPtr = f;
mFuncVar = variable;
}
void assess( )
{
mFuncVar->assess();
this->mValue = (*mFuncPtr)(*mFuncVar);
}
};
OK, the problem is how to use it. Main source looks like this:
int squared( Derived<int>& );
int squared( Derived<int>& d )
{
int val = d.value();
return val*val;
}
int main (int argc, const char * argv[])
{
Derived<int> object;
object.assess();
cout << object.value() << "\n" ;
DerivedFuncPtr<int> objectFP( squared, object ); // (*)
return 0;
}
I get an error at compilation on line (*)
Candidate constructor not viable: no known conversion from 'int (Derived<int> &)' to 'int (*)(Base &)' for 1st argument
Will I be forced to encapsulate the function pointer into a templated U (*)(Base&) class or am I missing something obvious here?
For the sake of esthetics and because I am really not familiar with most recent C++ evolutions, I'd prefer not to use boost libraries or C++11, but well, if it can't be helped...
Thank you.
DerivedFuncPtr<T>( T (*f)(Base&), Base& variable )
The first argument to your constructor is a pointer to a function that takes a Base& as an argument (and returns a T). That Base& argument to the function pointer doesn't match the argument to squared, which takes a Derived<int>&.
One way to fix this is to make DerivedFuncPtr a class template that take two template arguments, one the typename T you already have, and the other, some other typename that will replace the uses of Base in your data members and constructor.
BTW, you also get an error on mFuncVar = variable; in that constructor. mFuncVar is a pointer, variable is a reference.
Related
I'm trying to pass a class method to another class method using template, and cannot find any answer on how to do (no C++11, boost ok):
I simplified the core problem to :
class Numerical_Integrator : public Generic Integrator{
template <class T>
void integrate(void (T::*f)() ){
// f(); //already without calling f() i get error
}
}
class Behavior{
void toto(){};
void evolution(){
Numerical_Integrator my_integrator;
my_integrator->integrate(this->toto};
}
I get as error:
error: no matching function for call to ‘Numerical_Integrator::integrate(<unresolved overloaded function type>)’this->toto);
note: no known conversion for argument 1 from ‘<unresolved overloaded function type>’ to ‘void (Behavior::*)()’
Thank you.
Bonus: What about with arguments ?
class Numerical_Integrator{
template <class T, class Args>
double integrate(void (T::*f)(), double a, Args arg){
f(a, arg);
}
}
class Behavior{
double toto(double a, Foo foo){ return something to do};
void evolution(){
Foo foo;
Numerical_Integrator my_integrator;
my_integrator->integrate(this->toto, 5, foo};
}
Your question is not really about passing a class method as part of a template parameter.
Your question is really about correctly invoking a class method.
The following non-template equivalent will not work either:
class SomeClass {
public:
void method();
};
class Numerical_Integrator : public Generic Integrator{
void integrate(void (SomeClass::*f)() ){
f();
}
}
A class method is not a function, and it cannot be invoked as a function, by itself. A class method requires a class instance to be invoked, something along the lines of:
class Numerical_Integrator : public Generic Integrator{
void integrate(SomeClass *instance, void (SomeClass::*f)() ){
(instance->*f)();
}
}
You need to revise the design of your templates, and/or class hierarchies in order to resolve this first. Once you correctly implement your class method invocation, implementing a template should not be an issue.
I would like to get the templated type of the base class inside of an object that owns an instance of the derived class. The code snippet below won't work because Base and its ArbitraryType can't be referenced through DerivedString. (line marked with the exclamation point). However, it can most definitely be inferred from the type of its own template (OneOfTheDerivedTypes). In my case, I intend for AnotherObject with a defined template to be inherited from, so I don't want to just hardcode the return type to GetSomethingFromThingy().
// -------------------
// Provided by my API:
// -------------------
template <typename ArbitraryType>
class Base {
virtual void DoSomething(ArbitraryType);
};
template <typename OneOfTheDerivedTypes>
class AnotherObject<OneOfTheDerivedTypes> {
// Other functions that perform useful tasks are here so that
// classes that inherit from AnotherObject need not implement them.
void SomethingMagical();
// A function with a specific return type.
virtual DerivedString::Base::ArbitraryType GetSomethingFromThingy() = 0; /* ! */
protected:
OneOfTheDerivedTypes thingy;
};
// --------------------------------------
// Someone using my API would make these:
// --------------------------------------
class DerivedFloat : public Base<float> {
void DoSomething(float) override;
};
class DerivedString : public Base<string> {
void DoSomething(string) override;
};
class UsefulObject : public AnotherObject<DerivedString> {
// Knows the required return type of GetSomethingFromThingy() without
// needing to specify it as a template. Should throw compile-time error
// if you tried to override with a different return type. In other words,
// forces return type to be string because of use of DerivedString.
string GetSomethingFromThingy() override;
};
One solution to this is specify an additional template arg called ArbitraryType as seen below:
template <typename OneOfTheDerivedTypes, typename ArbitraryType>
class AnotherObject<OneOfTheDerivedTypes> {
virtual ArbitraryType GetSomethingFromThingy() = 0;
protected:
OneOfTheDerivedTypes thingy;
};
class UsefulObject<DerivedString, string> : public AnotherObject<DerivedString, string> {
string GetSomethingFromThingy() override;
};
The programmer then must specify both parameters where OneOfTheDerivedTypes is either DerivedFloat or DerivedString and ArbitraryType is float or string, respectively. Not a good solution because ArbitraryType is completely specified by the choice of OneOfTheDerivedTypes.
I think the extra template (ArbitraryType in AnotherObject) could be avoided by having Base return an instance of ArbitraryType in a public function (call it ReturnInstanceOfArbitraryType()) and use decltype(OneOfTheDerivedTypes::ReturnInstanceOfArbitraryType()) inside of AnotherObject. This seems inelegant because ReturnInstanceOfArbitraryType() is not useful otherwise (and must be public). Is this a case where the proper thing to do is to use a traits class? Is there a better solution? (Still getting the hang of some of this new C++11 stuff). Thanks!
Maybe I've misunderstood your question, but can't you just add a typedef to Base?
template <typename ArbitraryType>
class Base {
virtual void DoSomething(ArbitraryType);
using parameter_type = ArbitraryType;
};
And then you can refer to it:
template <typename OneOfTheDerivedTypes>
class AnotherObject {
// A function with a specific return type.
virtual typename OneOfTheDerivedTypes::parameter_type GetSomethingFromThingy() = 0;
};
And then the override in the derived type will enforce that the return types are the same (or covariant):
class UsefulObject : public AnotherObject<DerivedString> {
string GetSomethingFromThingy() override;
};
You could also add a static_assert if you want a more user-friendly error message:
class UsefulObject : public AnotherObject<DerivedString> {
using base_parameter_type = typename AnotherObject<DerivedString>::parameter_type;
static_assert(std::is_same<string, base_parameter_type>::value,
"mismatched parameter types");
string GetSomethingFromThingy() override;
};
If you can't modify the Base template it's also possible to detect the type using some metaprogramming. First, declare (but don't define) a function that can deduce the type T from Base<T>:
template<typename T> T* detect_base_parameter_type(Base<T>*); // undefined
Now define an alias template which takes one of the derived types as its template parameter, and uses the function above to find the template argument of its base-class:
template<typename DerivedT>
using base_parameter_t = typename std::remove_pointer<
decltype( detect_base_parameter_type(std::declval<DerivedT*>()) )
>::type;
This uses decltype to detect the return type of calling detect_base_parameter_type with a pointer to the derived type. That pointer will convert to a pointer to Base<T> (deducing whatever type T is for DerivedT) and the function's return type will be T*. Then we use remove_pointer to turn that into T.
Now you can use that alias template in your other classes:
template <typename OneOfTheDerivedTypes>
class AnotherObject {
// A function with a specific return type.
virtual base_parameter_t<OneOfTheDerivedTypes> GetSomethingFromThingy() = 0;
};
class UsefulObject : public AnotherObject<DerivedString> {
using base_parameter_type = base_parameter_t<DerivedString>;
static_assert(std::is_same<string, base_parameter_type>::value,
"mismatched parameter types");
string GetSomethingFromThingy() override;
};
template <typename... Arguments>
class CCallback
{
public:
template <class TargetClass>
CCallback(TargetClass * target, void (TargetClass::*targetMethod)(Arguments...))
{
}
};
struct TargetClassBase
{
protected:
void f() {}
};
struct TargetClassChild : TargetClassBase
{
void g() {}
void test()
{
CCallback<> callback(this, &TargetClassChild::f);
}
} child;
void main()
{
}
That code doesn't compile in MSVC 2013:
error C2660: 'CCallback<>::CCallback' : function does not take 2
arguments
I don't understand why I get this specific error, and how to make it work. There are no further details about the error logged by the compiler.
And, of course, I can't properly specify that the method belongs to the base class (&TargetClassBase::f)- taking a pointer to a non-public base method is forbidden.
Problem with your code is that compiler cannot deduce template type TargetClass in constructor for CCallback. This is because you pass arguments of types: TargetClassChild* and void (TargetClassBase::*)() to constructor. This is not a typo. Even if you write &TargetClassChild::f this expression still has type: pointer to function returning void in class TargetClassBase and not TargetClassChild as one could expect.
This kind of issues can be solved in two ways:
You could specify template type explicitly, but in this particular case it cannot be done because in C++ there is no way to explicitly pass template parameters to constructors as constructors don't have names (according to note in §14.5.2.5 of c++ standard).
Pass arguments of appropriate types to function. In this case simply cast your function to appropriate type like this static_cast<void (TargetClassChild::*)()>(&TargetClassChild::f)
Expanding on robal's perfectly correct answer, I've rewritten the constructor of my class so that I don't need a manual type cast:
template <class TargetInstanceClass, class TargetMethodClass>
CCallback(TargetInstanceClass * target, void (TargetMethodClass::*targetMethod)(Arguments...))
{
void (TargetInstanceClass::*targetInstanceMethod)(Arguments...) = static_cast<void (TargetInstanceClass::*targetInstanceMethod)(Arguments...)>(targetMethod);
}
I have the following class structure.
class Base {
protected:
template <typename Type>
Type convert(); // no implementation
public:
template <typename Type>
operator Type() {
Type t(convert<Type>());
// log conversion
return t;
}
}
};
class D1: public Base
{
protected:
template <type Type>
Type convert() {
Type t;
// Set t
return t;
}
};
template <typename Type>
class D2: public Base
{
public:
D2(Type v): value(v) {}
protected:
template <typename Type2>
Type2 convert() {
return value;
}
private:
Type value;
}
I am looking for a non-templated class that can be converted into a different type by a type conversion. I do not want to make Base a templated class, as that complicates how it is used.
I would like to force D2 to only allow Type2 and Type to be the same class. As written, if Type can be cast or converted to Type2, the code will work. I am being confused as to whether convert should be declared virtual (the answer appears to be no) and partial nested specializations.
I would like to provide a single specialization in D2 of
template <typename Type>
template <>
Type D2<Type>::convert<Type>() { ... }
but it is clear that this is not allowed.
I also tried
template <>
template <typename Type>
Type D2<Type>::convert<Type>() { ... }
which complained that "prototype for ‘Type D2::convert() const’ does not match any in class ‘D2’
I can't remove the template on convert because it is needed by D1.
Is there a way to get this to work?
At a high level, what I need is to be able to do the following:
Base *b = new D1(/*stuff*/);
int i = *b; // calls D1::convert<int> (a)
b = new D1(/*different stuff*/);
std::string s = *b; // calls D1::convert<std::string> (b)
b = new D2<int>(/* other stuff */);
int j = *b; // calls D2<int>::convert<int> (c)
b = new D2<std::string>(/*still other args*/);
s = *b; // calls D2<std::string>::convert<std::string> (d)
// This should fail as a compile time or link time error
b = new D2<int>(/*... */);
std::string s_fail = *b; // convertable to int, not string
// tries to call D2<int>::convert<std::string>, which is either
// not implemented or has an implementation that fails to compile,
// probably by casting an int to string (e)
The desired behavior is:
(a) Call a method in D1 that returns an int
(b) Call a method in D1 that returns a string
(c) Call a method in D2<int> that returns an int
(d) Call a method in D2<string> that returns a string
(e) Fail as early as possible, ideally at compile time or link time
Is that not possible in C++? I'm happy to rewrite everything except the public interface of Base, D1 not being templated and D2 being templated.
EDIT: Clarify the behavior that I want
I'm searching a solution for this for a few days now. Didn't find any question related enough to answer regrettably so here is my question.
Consider the next code:
// dummy class A
class A {
public:
void aFunction() { // <- this is the function I want to point at
cout << "aFunction() is called\n";
}
};
class B {
public:
template <class Class> // get a function pointer
void setFunction( void (Class::*func)() ) {
p_func = func;
}
void (*p_func)(); // the function pointer
}
int main() {
B obj;
objb.setFunction(&A::aFunction);
return 0;
}
I have a compilation error in setFunction() on p_func = func;:
cannot convert from 'void (__thiscall A::* )(void)' to 'void (__cdecl *)(void)'
And I don't seem to be able to get rid of it in any way. I know it has something to do with those invisible this pointers (__thiscall and __cdecl), but I don't know how to handle these. I tried making the member variable p_func a class template too (void (Class::*p_func)()) so it would have the same structure, but it that seems to be illegal to have 2 class templates in one class (why?), thus isn't the correct solution. This time the compiler complains about:
multiple template parameter lists are not allowed
This method (without the template) works perfectly on global functions (which is the workaround I currently use) and I saw the use of it in a library (sfgui), so it should be perfectly possible.
To have some context over why I'd want this: I'm trying to create a button. This button should be able to call whatever function I'd like. For now, I'd like it to call the start() function of an animation class I'm making.
p.s.: I know this example is useless since I can't run p_func: the function isn't static. I still need to add an object pointer (setFunction( void (Class::*func)(), Class* )), but that does not seem to be a problem. And I know about typedef to make a function pointer more readable, but not with a class template.
EDIT
After some more research I think the answer I need not the answer to this question, but rather another one. For once, I noticed that multiple template <class Class> is in fact allowed. However, it is not allowed on member variables since the compiler can't possibly know which class he'll need to use which probably is the reason for the error
multiple template parameter lists are not allowed
which is an odd description. Thanks anyway for the help, you did gave me a better insight.
You cannot convert a pointer-to-member Class::*func to a normal function pointer. They are of different types.
You should turn this:
void (*p_func)(); // the function pointer
into this:
void (class::*p_func)(); // the function pointer
You could also use a std::function<void()> and use boost::bind to bind it.
std::function<void()> fun = boost::bind(class::member_fun, args);
EDIT
What about making your B class a template so you can do this:
#include<iostream>
class A {
public:
void aFunction() { // <- this is the function I want to point at
std::cout << "aFunction() is called\n";
}
};
template<class T>
class B {
public:
void setFunction( void (T::*func)() ) {
p_func = func;
}
void (T::*p_func)(); // the function pointer
void callfunc()
{
(t.*p_func)(); //call pointer to member
}
private:
T t;
};
int main() {
B<A> obj;
obj.setFunction(&A::aFunction);
return 0;
}
Live Example
I found the complete answer myself while searching for a way to save *objects of an unknown type without using templates or void pointers which has been answered here. The solution is a bit dodgy, because you'll have to create a dummy parent which allows for certain conversions.
The idea is that you create a Parent and every object that is allowed to be pointed to must inherit from it. This way you can create a pointer as Parent *obj which can hold multiple types of objects, but of course only classes that inherit from Parent.
The same applies for function pointers. If you define your pointer as void (Parent::*func)() as member variable. You can ask the user a template function pointer template <class Class> setFunction( void (Class::*f)() ), which can hold any pointer to any class. Now you need to cast the function pointer to the desired class, Parent: static_cast<void(Parent::*)()>(f). Mind that this only works when Class inherits from Parent. Otherwise you'll get a compilation error.
Minimal Working Example
#include <iostream>
using namespace std;
// dummy class Parent
class Parent {};
// class A
class A : public Parent { // Mind the inheritance!
public:
A(int n) : num(n) {}
void print() { // <- function we want to point to
cout << "Number: " << num << endl;
}
int num;
}
// class B, will hold the 2 pointers
class B {
public:
B() {}
template <class Class> // will save the function and object pointer
void setFunction( void (Class::*func)(), Class *obj) {
function = static_cast<void(Parent::*)()>(func);
object = obj;
}
void execFunction() { // executes the function on the object
(object->*function)();
}
void (Parent::*function)(); // the function pointer
Parent *object; // the object pointer
}
int main() {
A a(5);
B b;
b.setFunction(&A::print, &a);
b.execFunction();
return 0;
}
I don't really like this solution. A better solution would be that class B could have a function where it returns a bool when the function needs to be executed. This way you could simply place an if statement in the main-function that executes the desired function.
A a(5);
B b;
while (;;) {
if (b.aTest())
a.print();
}
Where B::aTest() is declared as
bool B::aTest();
Hope this helps anyone that comes across the same problem. So it is perfectly possible but pretty dodgy in my opinion, and I don't encourage people using the first method.