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
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...
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. :)
Related
I'm not sure what I am asking for is possible.
I have a templated class called Controller. This is a variadic template class which takes multiple classes and can set their values as such.
Controller<ClassA,ClassB,ClassC>* myController = new Controller<ClassA,ClassB,ClassC>(*a,*b,*c);
myController->setValues(32);
This takes a bunch of different classes together and allows me to to set their values at the same time. setValues is a templated function which allows any type to be passed in. However, right now I am trying to modify my class so that I can set a value within the controller itself for easy retrieval. However this is the part that is proving difficult.
template<typename...Classes>
class Controller
{
public:
Controller(Classes&...objects) : objects(objects...){}
Controller(std::tuple<Classes&...> tup) : objects(tup){}
template<typename T>
void setValues(T value)
{
std::apply([&](auto&...x) { x.updateValue(value),...);}, objects); //calls the updateValue function for each class
}
private:
std::tuple<Classes&...> objects;
};
I want to add the following as a private variable T controllerValue; However, I know that I cannot simply declare T because we cannot define member templates and the compiler has no idea what to expect. Which then I tried to create a private struct:
template<typename T>
struct ControllerValue { T value; };
However, I cannot define a struct underneath that, because the same problem occurs. The compiler has no idea what type ControllerValue is. What I would like is something like this:
template<typename...Classes>
class Controller
{
public:
Controller(Classes&...objects) : objects(objects...){}
Controller(std::tuple<Classes&...> tup) : objects(tup){}
template<typename T>
void setValues(T value)
{
thisValue.value = value;
std::apply([&](auto&...x) { x.updateValue(value),...);}, objects); //calls the updateValue function for each class
}
template<typename T>
T getValue() const { return thisValue.value }
private:
std::tuple<Classes&...> objects;
template<typename T>
struct ControllerValue { T value; };
ControllerValue thisValue;
};
This will not compile at all for the same reason that the compiler has no idea what type ControllerValue should be. And this is where I am stuck. Is this even possible to do? If not, what is another way that I can make this work?
To clear up confusion, the use case would be something like this:
Controller<ClassA,ClassB,ClassC>* myController = new Controller<ClassA,ClassB,ClassC>(*a,*b,*c);
myController->setValues(32);
int commonValue = myController->getValue();
or
Controller<ClassA,ClassB,ClassC>* myController = new Controller<ClassA,ClassB,ClassC>(*a,*b,*c);
myController->setValues(32.3);
double commonValue = myController->getValue();
I think solving this exact problem is impossible in C++ (and still very cumbersome in languages with runtime generics). You can very easily create a polymorphic class that can only store any value:
class PolymorphicBase
{
public:
virtual ~PolymorphicBase() = default;
};
template <class T>
class PolymorphicObject : public PolymorphicBase
{
T value;
public:
PolymorphicObject(T value) : value(std::move(value))
{
}
};
A member of std::unique_ptr<PolymorphicBase> can sufficiently store any value, but how would such a value be retrieved? Probably the easiest is to expose the reference to PolymorphicBase and use dynamic type checks to see if the type is compatible with something you know, but what if you need the code to work for any type?
This is what lambdas with auto parameters are useful for. However, you would have to be able to pass such a lambda to a method on PolymorphicBase and implement that method in PolymorphicObject. This is impossible, since you cannot override a method template (it needs to be a template to accept a lambda) – that's where the compile-time and runtime parts of C++ clash. And there is simply no type in C++ that represents a function accepting any parameter (and knowing its type), which is a template by itself.
You can partially solve this by making the type of the lambda known to PolymorphicBase:
template <class Retriever>
class PolymorphicBase
{
public:
virtual void retrieve(Retriever func) = 0;
virtual ~PolymorphicBase() = default;
};
template <class Retriever, class T>
class PolymorphicObject : public PolymorphicBase<Retriever>
{
T value;
public:
PolymorphicObject(T value) : value(std::move(value))
{
}
void retrieve(Retriever func) override
{
func(value);
}
};
auto lambda = [](auto arg)
{
std::cout << arg << std::endl;
};
PolymorphicObject<decltype(lambda), int> obj(6);
PolymorphicBase<decltype(lambda)> &ptr = obj;
ptr.retrieve(lambda);
This is useful if you ever have only a single way to retrieve the value.
I don't think this is needed in most cases anyway. Usually you use a fixed set of types as the values, so you can use a variant there, or they all implement a common interface, or (as you've pointed out in the comments) you actually meant to move the type parameter from the method to the class (which allows you to check that all the types actually support the value earlier than originally).
However, I agree that in languages with generics/templates it is somewhat hard to have a method that can actually choose its result type in a generic fashion, without being controlled by outside parameters.
I am having trouble finding a fix for the following error, thrown when compiling a std::static_pointer_cast<>():
error: invalid static_cast from type ecse::EventSubscriptionManager<ecse::BaseEvent>* to type ecse::EventSubscriptionManager<TestEvent>*
I have the following hierarchy. In the end they will be filled with POD type members and will most likely become structs.
class BaseEvent {};
template <class E>
class Event : public BaseEvent, public Type<E> {};
class TestEvent : public Event<TestEvent> {};
I am currently working on the Subscribe function part of the EventManager, however when compiling I am receiving the error posted above. Note: E::ID() is defined in the class as Type and is used for identifying the class type.
template <class E>
class EventSubscriptionManager
{
public:
void Subscribe(std::function<void(E)> fptr);
private:
std::function<void(E)> event_function_;
};
class EventManager
{
public:
template <class E>
void Subscribe(std::function<void(E)> fptr)
{
std::shared_ptr<EventSubscriptionManager<E>> sub_manager_ptr;
auto sub_manager_iterator = sub_managers_.find(E::ID());
if(sub_manager_iterator == sub_managers_.end())
{
sub_manager_ptr = std::make_shared<EventSubscriptionManager<E>>();
}
else
{
sub_manager_ptr = std::static_pointer_cast<EventSubscriptionManager<E>>(sub_manager_iterator->second);
}
// Continue function...
}
private:
std::unordered_map<std::size_t, std::shared_ptr<EventSubscriptionManager<BaseEvent>>> sub_managers_;
}
I believe that the issue is that between the TestEvent and the BaseEvent there is the Event<E> class with the template, with TestEvent inheriting Event<TestEvent> instead of BaseEvent. Is this true? If so, how can I set up my hierarchy to allow for this type of casting?
If that is not the case, what is the issue with the above static cast?
I can tell you why it does not compile. This is because
EventSubscriptionManager<E>
is unrelated to
EventSubscriptionManager<BaseEvent>
So, according to point 1.) on the reference page,
static_cast<EventSubscriptionManager<E>*>((EventSubscriptionManager<BaseEvent>*)nullptr)
is ill-formed.
However, without knowing the background I can't tell what to do as a workaround.
Just: you have to relate the two classes, or choose a completely new design.
In order to do so, here is a minimal example why it fails which might be helpful:
struct Base {};
struct Derived : Base {};
template<typename T>
struct Foo {};
int main()
{
static_cast<Foo<Derived>*>((Foo<Base>*)nullptr);
}
You can try to improve on that.
In C++, there is no covariance or contravariance, there is no relationship between T<Base> and T<Sub>, even if there is one between Base and Sub.
You either need to build a common ancestor of different EventSubscriptionManager instances (e.g: EventSubscriptionManagerBase), and use that, or provide a converting constructor.
This is my first post. I have spent hours checking for a solution on my problem, searching link after link on SO, but none descried my problem exactly(the closest i could get were this and this). So, let's get to work!
Description: I must implement a collection of specialized classes, each one able to store a linked list of its type. Also (the tricky part), I must implement a collection manager, in a way that adding more specialized classes to the collection does not affect its code.
Let me explain what I have so far.
class IList {
public:
virtual IList& operator+( IList&) = 0;
virtual void print() = 0;
virtual int g_Size() const = 0;
//perfect till here
virtual void Push(const int&) = 0;//needs to be upgraded
virtual const int& operator[](int index) = 0;//needs to be upgraded
};
template<class T>
class Queue: public IList{
//internal stuff
public:
Queue();
int g_Size() const;
void print();
void Push(const T& cv);
const T& operator[](int index);
~Queue();
};//all implementation of Queue<T> is implemented and working, but removed for simplicity
class CIntList : public Queue<int>{
//stuff here, specialized on int
};
class C_Manager{
IList * classes[3];//notice the polymorphism, managing the class collection using a pointer to the common(base) interface
public:
void testing()
{
for (int i = 0; i < 3; i++)
classes[i] = new CIntList(i);
classes[0]->Push(1); classes[0]->Push(2); classes[1]->Push(1121); classes[2]->Push(12);
classes[0]->print();
classes[2]->print();
int a = classes[0]->operator[](1);
classes[1]->Push(a + a);
} //working fine
};
OK, so you`ll maybe ask, what is the question?
I do not want to redeclare the Push and operator[] (or any other function that uses the template as argument) for all of my classes specializations. More exactly, if I want to add, let's say,
class CFloatList: public Queue<float>
{
//float stuff goes here
};
I must also modify IList to
class IList {
public:
virtual IList& operator+( IList&) = 0;
virtual void print() = 0;
virtual int g_Size() const = 0;
//perfect till here
virtual void Push(const int&) = 0;//used for int
virtual const int& operator[](int index) = 0;//used for int
//NEW DECLARATION FOR FLOAT
virtual void Push(const float&) = 0;//used for float
virtual const float& operator[](int index) = 0;//used for float
};
How can I avoid these redeclarations? I need sort of "virtual function templates", but this is not supported in C++.
Is my approach wrong?
Sorry for not highlighting the c++ syntax, this is my first post and I only managed to format it in code blocks. Thank you for your time!
EDIT #1
A BETTER SOLUTION(as suggested by jaggedSpire - many, many thanks)
I have modified IList to
class IList {
public:
virtual IList& operator+( IList&) = 0;
virtual void afis() = 0;
virtual int g_Size() const = 0;
//templates
template<typename T>
void Push(const T& arg) //WORKS PERFECTLY
{
Queue<T>* cast = dynamic_cast<Queue<T>*>(this);
cast->Push(arg);
}
template<typename T>
const T& operator[](int index) //error
{
Queue<T>* cast = dynamic_cast<Queue<T>*>(this);
return cast->operator[](index);
}
};
and void C_Manager::testing() to
class C_Manager{
public:
void testing()
{
IList * a = new CIntList(1);
a->Push(200);//WORKS PERFECTLY
int c = a->operator[](0); //ERROR
}
};
and it produces these errors
Error C2783 'const T &IList::operator [](int)': could not deduce template argument for 'T'
Error C2672 'IList::operator []': no matching overloaded function found
intellisense: no instance of function template "IList::operator[]" matches the argument list
Basically, it complains about every possibly templated function that has a T-related return type. How can I fix this to make my manager truly polymorphic?
First, let's review your requirements:
Have a non-templated polymorphic base class, IList
Have a class template, Queue<T> inherit from the base class to implement it.
Be able to specialize the Queue<T> however you want
Presumably, given you're returning void from push, and const T& from operator[], you want to signal errors with an exception.
Pass in a parameter of a specific type to the base class IList, and have the resulting behavior depend on whether the underlying type of Queue<T> matches the type of the given parameter.
This last bit is the key: you're trying to choose behavior of a function based off of both the runtime type of the caller and the static type of the argument. However, which type actually matches the T in the implementing Queue<T> is determined at runtime.
Runtime determination of behavior based off of the runtime types of two objects (because the argument is known at runtime as well as compile time) is what multi-methods are for. C++ doesn't have native multi-method support, but it can be pieced together with dynamic_cast
I picked up on the similarity to your present issue through this answer, which provides a wonderful array of links for more details on implementing (and implementations of) full multi-method functionality in C++.
Now, a brute-force/naive implementation of multi-methods in C++ would require testing the arguments for every possible implementing type from a list of implementing types. This is something you've also indicated you don't want, but not to worry: you won't need to. This is because we only want to test one circumstance, rather than the many required of a typical multi-method situation. We're handed the type of the argument to add at compile time, when we can conveniently use that information to find the type of the only destination type we're interested in.
For a supplied type of T, we want to test whether the type we're dispatching to is really Queue<T>.
To do that, we're going to use the same test used in the simpler multi-method implementations: dynamic_cast. Specifically, we're going to cast the this pointer to the type we're testing for, using the provided argument type as the source for the template argument required.
Be warned: this means that implicit conversion between types won't happen without an explicit template argument. If you pass in a string literal to your std::string container and don't explicitly specify that you want a std::string container, it's going to look for a container that holds character arrays the length of your string literal, and detect none. They're different types, after all.
With that said, let's get to the code. For an interface Parent that is implemented by a variety of Child<T>, you can use this to get T specific behavior from a Child<T> accessible only through a Parent interface:
class Parent{
public:
template <typename T>
void foo(const T& t);
virtual ~Parent(){}
};
template <typename T>
class Child : public Parent{
public:
void foo(const T& t);
};
// must be after the definition of the Child template,
// because dynamic_cast requires a complete type to target
template <typename T>
void Parent::foo(const T& t){
// throws on bad conversion like we want
auto castThis = dynamic_cast<Child<T>&>(*this);
// if execution reaches this point, this is a Child<T>
castThis.foo(t);
}
With:
template<typename T>
void Child<T>::foo(const T& t){
std::cout << typeid(T).name() << ": " << t << '\n';
}
int main(){
Parent&& handle = Child<int>();
try{
handle.foo<int>(3);
handle.foo<char>(0);
handle.foo<std::string>("Hello!");
}
catch(std::bad_cast e){
std::cout << "bad cast caught\n";
}
}
We get the following output on both g++ 5.2.0 and clang 3.7
i: 3
bad cast caught
Which is what we wanted.
Once you have the simple polymorphic interface presented here, implementing your collection should be easy. I'd go with a wrapper class around a std::vector<std::unique_ptr<Parent>> myself, but that decision is ultimately up to you.
Now, because this wasn't enough of a wall of text, some notes:
Throwing an exception is not good for standard control flow. If you don't actually know whether or not an argument matches the underlying type via some external logic, you want some other form of error handling. dynamic_cast may be used to cast both references and pointers. Casting a reference to an object not of the target type will throw std::bad_cast. Casting a pointer will return a null pointer.
Using the same name for a member function in a derived class as a templated member function calling that member function in the base class works because of the way name lookup works in C++. From this answer:
The basic algorithm is the compiler will start at the type of the current value and proceed up the hierarchy until it finds a member on the type which has the target name. It will then do overload resolution on only the members of that type with the given name. It does not consider members of the same name on parent types.
So the lookup for foo will start in Child<T>, and since it finds a member function with that name inside Child<T>, it doesn't examine Parent or call the dispatching function again.
3. I would consider why I'm doing this carefully before actually using this sort of workaround.
I have some question on derived class templates. I have base and derived class templates like this:
// This is base class
class CParameter {
public:
CParameter(std::string name) : name(name) {}
// virtual ~CParameter() {} // deleted for good design:)
public:
std::string name;
};
...
// This is derived class
template <typename T>
class CTemplateParameter : public CParameter {
public:
CTemplateParameter(std::string name) : CParameter(name) {}
public:
T parameter;
};
and I declare some type parameters the push them to base class vector
//Base class parameters
std::vector<CParameter*> parameters; // !
CTemplateParameter<CMatrix4<float>> mat4;
CTemplateParameter<CVector3<float>> vec3;
CTemplateParameter<float> flt;
parameters.push_back(mat4);
parameters.push_back(vec3);
parameters.push_back(flt);
I have template SetParameter function:
// This method moved to CParameter base class
template <typename T>
bool SetParameter(const CTemplateParameter<T>& param) {
// switch(typeof(T)) {
// set parameter
if (std::is_same<T, int>::value)
// gLUniform1i(...)
else if (std::is_same<T, CMatrix4<float>>::value)
// glUniformMatrix4fv(..)
...
}
So my questions:
1) How to set all parameter individual?
// Notice this function is not template
void SetAll() {
for each parameter
SetParameter(parameter[i])
}
2) Without enum, can I get type of parameter and create a type in run time? Like:
Pseudo code:
//get type of parameter[i]
//create a parameter from
T type = GetTypeofParameter(parameter[i]);
CTemplateParameter<type> newType;
3) Can i get a derived class type like this or how to cast?
CTemplateParameter<void*>* p = dynamic_cast<CTemplateParameter<void*>>(parameters[i]);
Thanks so much.
My comments seem to have pushed ADesignersEncyclopedia away from a template/virtual mix but not toward a practical alternative. The original question doesn't give enough info to decide whether there is a practical alternative. Lacking such a practical alternative, do the virtual/template mix correctly (with CRTP) rather than reject it entirely:
In your target class, you want setParameter in two forms, neither of which is a template. The first form dispatches to the setParameter inside the parameter class, which dispatches back to the second form in the target class:
bool SetParameter(const CParameter& param) {
return param.SetParameter( *this );
}
The second form is overloaded on the value type:
bool SetParameter(int value) {
// whatever
}
bool SetParameter(CMatrix4<float> const& value) {
// whatever
}
...
In your parameter base class, you want SetParameter pure virtual
class CParameter
{
...
virtual bool SetParameter( TargetType& ) const = 0;
...
};
Then you need a CRTP base class, which should be derived from your simple base class:
template<class ActualType>
class CRTPParameter : public CParameter
{
CRTPParameter(std::string name) : CParameter(name) {}
ActualType* This() {return static_cast<ActualType*>(this); }
ActualType const* This() const {return static_cast<ActualType const*>(this); }
// various things including
ActualType* clone() const { return new ActualType( *This() ); }
bool SetParameter( TargetType& target ) const
{ return target.SetParameter( This()->parameter ); }
};
Then your template class derives from your CRTP class
template <typename T>
class CTemplateParameter : public CRTPParameter<CTemplateParameter<T> > {
public:
typedef CRTPParameter<CTemplateParameter<T> super;
CTemplateParameter(std::string name) : super(name) {}
If everything else is simple enough, then the whole CRTP scheme might be overkill and you could just move clone and SetParameter from CRTPParameter to CTemplateParameter and go back to not having CRTPParameter.
But in my experience with such structures, things soon get messy in CTemplateParameter in ways that a best dealt with by the extra layer.
1/
You should define setParameter in Cparameter as an abstract method, and implement it in your template class.
2/
I suggest to use a clone(or a factory) method, defined as I suggested in 1/. In this method you can copy(or create) your object then define it.
3/
No. You can't cast CtemplateParameter<float> to CtemplateParameter<void*>
1) How to set all parameter individual?
You can't iterate and set the value for all of them, unless you know the type. And put a huge amount of dynamic_cast would not be the solution either as it is not scalable.
One solution would be to keep a map of std::function. These function would not take any parameter and return nothing. They will set the parameter with the right values. Pushing into the vector would be like this:
std::map<CParameter*, std::function<void()>> mySets;
// ...
mySets.emplace(¶meter, [ObjectThatGiveMeNext, ¶meter]() {
parameter.setParameter(ObjectThatGiveMeNext.get());
});
Even if you contain the parameter, it is not your main container for parameters. It is only to keep track of which parameter is associated to which function.
The ideal would be to create this function when you create the parameter, because you know the type of the parameter there.
Another solution would be to create a virtual function updateValue that would call setParameter with this.
2) Without enum, can I get type of parameter and create a type in run time?
That is not really possible it's a context where you don't know the type, so you must either know the type (a switch case) or rely on polymorphic behaviour. I think the best here is to rely on polymorphic behaviour.
I would add a virtual function clone for that. Maybe not the famous straight clone function, but a clone function that return both the parameter and the function to set it's value. Something a bit like that:
std::tuple<std::unique_ptr<CParameter>, std::function<void()>> clone();
Consider a typedef or using in this case as the type is loooooong.
3) Can i get a derived class type like this or how to cast?
No you can't. You would need to convert the instance of your class to another, not related type. I would not do that. Instead, keep your code that deal with your specific derived class where you explicitely know the type and keep the generic code generic (eg: not trying to know the type). This is the best advice I can tell you right now.
Here's what I'm trying to accomplish:
I'm trying to created a linked list of various types. In order to accomplish this, I figured polymorphism would be a great way to go about.
I have two classes, AttributeBase and Attribute. AttributeBase is used by AttributeSet, which just stores the start and end points of the linked list of Attribute<T>'s (as AttributeBase*'s) and does modification on the list. AttributeBase is a base class of Attribute<T> that is only in the design for the sake of making generic pointers. Attribute<T>, of course, is the specific type of AttributeBase where the actual value is stored. The main data of each Attribute<T> is an inherited string (the attribute's name, or 'key' if you will) and a value of type T.
So, thus far I have (simplified):
class AttributeBase
{
public:
AttributeBase() = delete;
AttributeBase* GetNext() { return next; };
AttributeBase* GetPrev() { return prev; };
std::string GetName() { return name; };
//Sometimes I need to get/set the value stored in a derived class
//But, how would I define the function here since the return
//type is of type T as defined in Attribute?
virtual ???? GetValue = 0;
virtual void SetValue(????) = 0;
friend class AttributeSet;
private:
AttributeBase* next = nullptr;
AttributeBase* prev = nullptr;
std::string name;
};
template <class T>
class Attribute : public AttributeBase
{
public:
Attribute( std::string _name, T _value ){ name = _name; value = _value };
T GetValue(){ return value; };
void Setvalue(T){ value = T; };
private:
T value;
};
class AttributeSet
{
public:
template <class T>
void Add(std::string,T); //Add an Attribute<T>(std::string,T) to the list
void Delete(std::string);
bool Contains(std::string _name); //Scan the list to determine if an
//attribute with name of _name exists
template <class T>
T Get(std::string); //Scan the list for 'name' and return
//AttributeBase*->GetValue()
private:
AttributeBase* start = nullptr;
AttributeBase* end = nullptr;
}
Since I tried to keep AttributeBase generic and non-templated (to avoid strongly-typed start and end pointers in AttributeSet), this brings up a problem. How do I specify an as-of-yet unspecified return type for the virtual function BaseAttribute::GetValue(). I first tried using auto, got a compile error.
Being as no instances of AttributeBase are ever actually created (and the default constructor deleted) I figured it would be possible to leave out GetValue and define it in the derived class. However, if I try *AttributeBase->GetValue() it errors out since GetValue() isn't defined in AttributeBase, only the subclasses. You would think the compiler would know that the pointer has to point to a derived class (the only derived type) since AttributeBase cannot be directly constructed.
So, in order to use GetValue() I have to know the type of the previous value ahead of time to be able to cast the AttributeBase* to an Attribute*. This would be trivial if AttributeBase itself were templated and contained a value T type. I could then just access AttributeBase*->type to determine the type of pointer I need to cast. However, like I said, templating AttributeBase destroys the intended use of the object.
More than likely, I'm going about this in a completely wrong way (yet again). But at this point I am stuck for ideas. Any help would be appreciated!
So a truly general solution doesn't exist. You just can't get any arbitrary type from a base class because all your overrides of your base class virtual function have to have the same return type.
That leaves you two options.
First, you can decide in advance that you're going to have your list hold any object that derives from some common base type. This will severely limit what you can put into your list, but at least you have full freedom with those objects once they're there.
Second, depending on what you want to actually do with the objects once they're in your list, you can look at the new Boost.TypeErasure library. If all you need to do with list is, say, output them all, or some either small amount of operations, this can help you get there.
Since the signatures of GetValue and SetValue depend on a type, they need to be templates. But they can be template members without requiring a class template.
class AttributeBase
{
public:
template <typename T> T GetValue() const;
template <typename T> void SetValue(T);
//...
};
template <typename T>
T AttributeBase::GetValue() const
{
return dynamic_cast<Attribute<T>&>(*this).GetValue();
}
template <typename T>
void AttributeBase::SetValue(T val)
{
dynamic_cast<Attribute<T>&>(*this).SetValue(val);
}
template <typename T>
T AttributeSet::Get(std::string const& name) const
{
// (assuming a private helper method Find().)
const AttributeBase* attr = Find(name);
if ( !attr )
throw std::invalid_argument("attribute not in set");
return attr->GetValue<T>();
}
One gotcha, though: these functions will all throw an exception if you happen to use the wrong type. And SetValue might automatically deduce its template argument, and might do so incorrectly. For example, if a is a AttributeBase& reference which is really an Attribute<long int>, then a.SetValue(1) is the same as a.SetValue<int>(1), which will throw. The correct expression would be a.SetValue<long int>(1) (or a.SetValue(1L), but I'd prefer the explicit template argument).