Struct Inheritance Hierarchy - Dynamic down-cast - c++

i have following 2 structs which are defined within a template class and a container, which holds elements of the base class, as follows:
class template<typename T1, typename T2>
class TTestDataObject
{
private:
//base element
struct SDataContainerElement
{
T1* m_sData;
};
//derived element
struct SInvalidDataContainerElement : SDataContainerElement
{
int m_eExpectedErrorCode;
};
//container holding base elements
typedef std::map<T2, SDataContainerElement* > TDataContainer;
TDataContainer sCInvalidData;
public:
typedef TDataContainer::const_iterator TDataConstIterator;
}
I want to implement a method, which can extract information from this container, doing different things depending on which element it has, the base class (SDataContainerElement) or the derived class (SInvalidDataContainerElement) and I implemented it as follows :
template<typename TDataStruct, typename TDataEnum>
int TTestDataObject<T1, T2>::eGetExpectedError(T2 eIndex)
{
TDataConstIterator sElement = sCInvalidData.find(eIndex);
if(dynamic_cast<SInvalidDataContainerElement*>((sElement->second)) == NULL)
return -1;
else
return static_cast<int>(sElement->second->m_eExpectedError);
}
Trying to compile leads to the following error :
E2307 Type'TTestDataObject<BEREICHTYP,eTestDataBereichTyp>::SDataContainerElement' is not a defined class with virtual functions
I don't understand this. Can anybody explain this error to me and show me a solution pls?
Thanks in advance!

As described in FAQ: Why does dynamic_cast only work if a class has at least 1 virtual method? , i need a virtual method, like a virtual destructor, in the base class.
To make this code work, i simply added
virtual ~SDataContainerElement(){};
to the base - struct. Thanks!

Related

c++ abstract base with datatype will be defined in derived class

I want to have a base class with datatypes that will be defined in derived class.
pseudo code
class Base{
public:
void Enroll(vector<int> v){
feature_list.emplace_back(ExtractFeature1(v));
}
vector<double> Compare(vector<int> v){
FeatureType2 ft2 = ExtractFeature2(v);
vector<double> scores;
for (auto &ft1:feature_list){
scores.emplace_back(Compare(ft1, ft2));
}
return scores;
}
protected:
vector<FeatureType1> feature_list;
virtual FeatureType1 ExtractFeature1(vector<int> v)=0;
virtual FeatureType2 ExtractFeature2(vector<int> v)=0;
virtual double Compare(FeatureType1 f1,FeatureType2 f2)=0;
}
So each derived class will implement a different way of extracting and comparing features.
I don't know how to do set some kinds of placeholder type for FeatureType1 and FeatureType2 in Base class and then force Derived class to define them. Any advice or guidance would be greatly appreciated.
I want to have a base class with datatypes that will be defined in derived class.
Well, you can't quite do that: The base class has to be completely defined for you to derive classes from it.
What you can do, however, is use the Curiously-Recurring Template Pattern (CRTP):
template <typename T>
class Base {
using data_type = typename T::data_type;
// this will be a different type for each T - and
// we don't need to know it in advance
void Enroll(const vector<int>& v){
// implementation that can depend on T in many ways.
// Specifically, you can use `data_type`.
}
vector<double> Compare(const vector<int>& v){ /* ... */ }
// ...
};
class SomeDerived : Base<SomeDerived> { /* ... */ };
class AnotherDerived : Base<AnotherDerived> { /* ... */ };
The classes SomeDerived and AnotherDerived don't actually have the same base class, but their base classes are instantiations of the same template, so you avoid code duplication. And - you have the "base" class using types defined in the derived class, as long as they're defined in the same way.
Edit (thanks #Aconcagua): You might not need to go as far as the full CRTP. If the only thing your base class needs to know regarding the derived class is the data type, then just template the base class on that, i.e.
template <typename DataType>
class Base {
using data_type = DataType;
// ... as before...
};
class SomeDerived : Base<some_data_type> { /* ... */ };
class AnotherDerived : Base<another_data_type> { /* ... */ };

Inheritance hierarchy from template arguments

I'd need to create a type erasure pattern which would allow to retrieve all the contained objects inheriting from a given base class. As far as I know, a popular type erasure like boost::any allows to retrieve an object with any_cast only if the requested and contained classes matches exactly, so it won't fit my needs.
I could solve the problem with template class which mimicks the inheritance relationship of the template argument. For example, TemplateClass<Derived> should be a child of TemplateClass<Base>, so that the following example would work:
// Suppose all clases have virtual destructors so that vtable and RTTI info are available
class ObjectWrapperBase {
}
template<class DataType>
class ObjectWrapperT: public ObjectWrapperBase {
public:
ObjectWrapperBase(T* ptr): dataObjPtr(ptr){}
DataType *dataObjPtr;
}
class Base{}
class Derived: public Base{}
class NotDerivedFromBase{}
int main(){
std::vector<ObjectWrapperBase*> v;
v.push_back(new ObjectWrapperT<Base>(new Base));
v.push_back(new ObjectWrapperT<Derived>(new Derived));
v.push_back(new ObjectWrapperT<NotDerivedFromBase>(new NotDerivedFromBase));
// Now suppose I want to retrieve all the Base and children objects in v
// If ObjectWrapperT<Derived> is a child of ObjectWrapperT<Base> I can write:
for(int i = 0; i < v.size(); i++){
ObjectWrapperT<Base> *wPtr = dynamic_cast<ObjectWrapperT<Base>*>(v[i]);
if(wPtr){
Base *basePtr = wPtr->dataObjPtr;
}
}
}
Is there a pattern to achieve this behavior? Or eventually another solution?
Thanks.
You cannot do exactly what you want, but you can get something closer with templates and operators.
As a minimal, working example:
#include<type_traits>
template<typename D>
struct S {
S(D *d): d{d} {}
template<typename B, typename = std::enable_if_t<std::is_base_of<B, D>::value>>
operator S<B>() {
return {d};
}
private:
D *d;
};
struct B {};
struct D: B {};
struct A {};
int main() {
S<D> sd{new D};
S<B> sb = sd;
// S<A> sa = sd;
}
If you toggle the comment to the last line, it won't compile anymore for A is not a base of B.
I don't know if it is what you're looking for but you can do something like this:
template <typename T>
class Derived : public T
{
};
And then instantiate it with a mother class named Base like this:
Derived<Base> object;
You will finally get this from Base:
class Derived : public Base
{
};

static_pointer_cast through inheritance and template

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.

How to obtain similar result as virtual function template c++

Here's my issue, I'm trying to create a base class which can get a reference to a queue member in a derived class. I have two template functions in my base class :
class Base
{
template<TYPE type>
virtual void foo(std::queue<TYPE>*& typeQueue) //I know virtual isn't allowed
{
//do nothing as it's general case
}
template<typename TYPE>
void bar(TYPE type)
{
std::queue<TYPE>* typeQueue;
foo(typeQueue);
//... do some stuff with type
}
}
and a derived class which would theoretically be able to specialize the function foo for any types
class Derived : public Base
{
public:
template<>
void foo<int>(std::queue<int>*& m_integerQueue)
{
integerQueue= &m_integerQueue;
}
template<>
void foo<double>(std::queue<double>*& doubleQueue)
{
doubleQueue = &m_doubleQueue;
}
private:
std::queue<int> m_integerQueue;
std::queue<double> m_doubleQueue;
}
code above is more of about an ideology then a code to take word for word, I'd like the function bar to call the according function foo in derived class based on the type specified when bar is called. Of course this solution isn't working and the problem here is that we can't make template functions virtual.
I'm not sure if it's an error of design, but that's the general idea and I couldn't find an appropriate solution anywhere so I posted my own question here.
EDITED to make my problem clear

Passing vectors of derived shared pointers?

What is the proper way to allow vectors of shared pointers to a derived class to get passed to a function which is expecting a vector of shared pointers to a base class without performing a copy?
Here is the code:
#include <string>
#include <vector>
#include <memory>
class Base {
public:
std::string Name;
};
using BaseList = std::vector<std::shared_ptr<Base>>;
class Derived : Base {
};
using DerivedList = std::vector<std::shared_ptr<Derived>>;
class BaseHandler {
public:
void process( BaseList list ) {
}
};
int main() {
DerivedList list;
BaseHandler bh;
bh.process( list );
}
Code Link: http://coliru.stacked-crooked.com/a/5a5b18ba3b2a4f08
EDIT: DOH!!! I posted the wrong one. Sorry about that...here is the shared_ptr one.
You may try this.
template <class T,
class SharedPtr = typename T::value_type,
class Element = typename SharedPtr::element_type,
class IsDerived = typename std::enable_if<std::is_base_of<Base, Element>::value, void*>::type
>
void process(const T& t) { std::cout << "process" << std::endl; }
The key ideas are:
Instead of accessing the elements through base class pointers, we can access the them through their concrete type information known by the compiler.
This function template uses a trick called "SFINAE" to check whether the parameter is a container of smart pointer of derived class.
Follow up:
Conversion from "container of shared pointer to derived class" to "container of shared pointer to base class" is possible and not very difficult. However, I concern whether the design choice of using "container of shared pointer to base class" will give you acceptable performance.
Let's discuss how to do it first.
We can create a std::shared_ptr<Base> object from each std::shared_ptr<Derived> object by using std::static_pointer_cast.
To apply std::static_pointer_cast on everything entries of the list, we can use std::transform.
If you have many derived classes, the conversion can be made available to every derived classes by using a function template with SFINAE check as mentioned.
So, the code looks like:
DerivedList derivedList;
// Put items into derivedList
BaseList baseList;
baseList.reserve(derivedList.size());
std::transform(std::begin(derivedList), std::end(derivedList), std::back_inserter(baseList),
[](const std::shared_ptr<Derived>& shptr)
{
return std::static_pointer_cast<Base>(shptr);
});
BaseHandler bh;
bh.process(baseList);
Or:
class BaseForwarder
{
public:
template <class T,
class SharedPtr = typename T::value_type,
class Element = typename SharedPtr::element_type,
class IsDerived = typename std::enable_if<std::is_base_of<Base, Element>::value, void*>::type
>
void process(const T& derivedList)
{
BaseList baseList;
baseList.reserve(derivedList.size());
std::transform(std::begin(derivedList), std::end(derivedList), std::back_inserter(baseList),
[](const SharedPtr& shptr)
{
return std::static_pointer_cast<Base>(shptr);
});
BaseHandler bh;
bh.process(baseList);
}
};
However, this approach has quite a lot of performance penalty.
A new list of pointer to base class has to be created for each list of pointer to derived class. It spends a lot of time and memory to construct the new list.
The objects are accessed through pointers. This indirection slow things down.
Since the objects are not allocated into compact data structure, cache misses will be severe.
I suggest you to evaluate the performance to see whether this approach is acceptable. Otherwise, it is better to consider some other design choices.