I would like some advice on how I can solve an interesting problem I have.
The problem is to have two storage containers, of which the user selects which one to use for the remainder of the program (edit: at runtime). The two containers are Vector and List and store an object type we are to define. These two containers can be accessed using any means you desire (pop/[i]/...) How would you go about solving this problem?
Below is my best (almost working) solution, but I would really like to see what solutions more skilled C++ professionals have. As previously stated, I am really interested if I am taking the right approach. I have more than typical free time this semester and I intend to use it to really improve my c++ abilities. Thanks for your feedback.
Version 1
To start, I have a boolean flag,
bool using_vector = true; // what storage container was selected?
Second comes my two containers,
list<Question> q_llist;
vector<Question> q_vec;
Third my implementations for accessing the containers,
(still haven't figured out how make get_question() work in a graceful way, and I am not fond of the current route I am taking at the moment)
const Question& get_question(){
Question q = (using_vector) ?
q_vec.back() : q_llist.back();
(using_vector) ?
q_vec.pop_back() : q_llist.pop_back();
return q;
}
int questions_size(){
return (using_vector) ?
q_vec.size() : q_llist.size();
}
void push_back_question(Question& q){
if(using_vector){
q_vec.push_back(q);
}else{
q_llist.push_back(q);
}
}
Version 2
Note: Please use the tag "#v2" when referencing.
I decided to attempt the polymorphism approach. How does this implementation look?
/**
* using polymorphism to implement a parent class "Container"
* depending on user selection, reference C_Vec or C_List
*/
class Container {
protected:
list<Question> qlist;
vector<Question> qvec;
public:
void push_back(Question& q){/** do nothing */}
void pop_back(){/** do nothing */}
int size(){/** do nothing */}
Question& back(){/** do nothing */}
};
class C_Vec: public Container{
public:
void push_back(Question& q){qvec.push_back(q);}
void pop_back(){qvec.pop_back();}
int size(){return qvec.size();}
Question& back(){return qvec.back();}
};
class C_List: public Container{
public:
void push_back(Question& q){qlist.push_back(q);}
void pop_back(){qlist.pop_back();}
int size(){return qlist.size();}
Question& back(){return qlist.back();}
};
int main(){
Container *store;
char user_in;
cout << "Before we begin please select a storage container:" << endl
<< "a) Linked List" << endl
<< "b) Vector" << endl << ':';
cin >> user_in;
if(tolower(user_in) == 'a'){
C_List l;
store = &l;
}else{
C_Vec v;
store = &v;
}
}
You have several options. If you need to decide at runtime which container to use, polymorphism (inheritance) might work well.
#include <vector>
#include <list>
#include <memory>
struct Question {};
// runtime
struct Question_container {
virtual const Question& get_question() = 0;
virtual int questions_size() = 0;
virtual void push_back(const Question&) = 0;
virtual ~Question_container() = default;
};
struct Vector_question_container : Question_container {
const Question& get_question() override { return qv.back(); }
int questions_size() override { return qv.size(); }
void push_back(const Question& q) override { qv.push_back(q); }
private:
std::vector<Question> qv;
};
struct List_question_container : Question_container {
const Question& get_question() override { return qv.back(); }
int questions_size() override { return qv.size(); }
void push_back(const Question& q) override { qv.push_back(q); }
private:
std::list<Question> qv;
};
int main()
{
// some how figure out which container to use
std::unique_ptr<Question_container> qc{new Vector_question_container()};
}
If you can make the choice at compile-time, you could make the underlying sequence a template (or even template template) argument.
// CompileTime
template<typename Sequence>
struct Question_container_c {
const Question& get_question() { return s.back(); }
int questions_size() { return s.size(); }
void push_back(const Question& q) { s.push_back(q); }
private:
Sequence s;
};
int main()
{
Question_container_c<std::list<Question>> qlc;
Question_container_c<std::vector<Question>> qvc;
return 0;
}
Although you could also just make your algorithm work on iterators and leave the choice of the container to the user. This might be hard for some methods such as your push_back, but it doesn't actually do anything else then the normal push_back already provided.
To complement #pmr's answer, if you want to do it in an idiomatic way, you can create an adapter interface:
class IContainer {
public:
virtual ~IContainer() {}
virtual void push_back(const Question & q) = 0;
virtual void pop_back() = 0;
virtual const Question & back() const = 0;
virtual unsigned int size() const = 0;
};
And a generic implementation:
template <class T>
class Container: public IContainer {
private:
T m_container;
public:
virtual void push_back(const Question & q) {
m_container.push_back(q);
}
virtual void pop_back() {
m_container.pop_back();
}
virtual const Question & back() const {
return m_container.back();
}
virtual unsigned int size() const {
return m_container.size();
}
};
So you can do this:
std::unique_ptr<IContainer> pctr;
if (choice) {
pctr.reset(new Container<std::vector<Question>>);
}
else {
pctr.reset(new Container<std::list<Question>>);
}
std::cout << pctr->size();
I suppose the best way for your approach is to use iterators instead. Iterators are invented as a container abstraction in mind (sure thing, you can't abstract by 100% due different behavior of containers but anyway you have a solution better than nothing).
Related
See code below for the details, but the underlying scenario is as follows. I have a container (a session) that I can place objects in and pull out from.
Similar to:
std::shared_ptr<Tiger> t = ...;
session.store("tigers/1", t);
std::shared_ptr<Tiger> t2 = session.load<Tiger>("tigers/1");
With both functions defined as:
class Session {
template<class T>
void store(std::string id, std::shared_ptr<T> instance);
template<class T>
std::shared_ptr<T> load(std::string id);
}
Note that a session can store heterogeneous types, but at store and load time I statically known what the type of the variable is.
My problem is that I run into a situation where the user wants to put a Tiger into the session but checks out a base type, instead. For example:
session.load<Animal>("tigers/1");
Right now, I'm effectively storing the data as void* inside the session and use reinterpret_cast to get them back to the user provided type. This... works, as long as everything is trivial, but when we get to a slightly more complex situation, we run into issues.
Here is the full code demonstrating my issue:
struct Animal
{
virtual void Pet() const = 0;
};
struct IJumpable
{
virtual void Jump() const = 0;
};
struct Tiger : Animal, IJumpable
{
void Pet() const override
{
std::cout << "Pet\n";
}
void Jump() const override
{
std::cout << "Jump\n";
}
};
int main()
{
auto cat = std::make_shared<Tiger>();
// how the data is stored inside the session
auto any_ptr = std::static_pointer_cast<void>(cat);
// how we get the data out of the session
auto namable = std::static_pointer_cast<IJumpable>(any_ptr);
namable->Jump();
std::cout << std::endl;
}
If you run this code, you'll see that it runs, but instead of calling Jump, it calls to Pet. I understand that this is because of the wrong virtual method table being used, since I'm effectively calling reinterpret_cast on `void*.
My question is if there is a good way to handle this scenario in C++. I've looked around and didn't see anything that matches what I need.
Everything I found about heterogeneous containers always assumed a shared base class, which I don't have nor want. Is this possible?
You could make the user provide you with the correct casting trek to follow:
class Session {
template<class T>
void store(std::string id, std::shared_ptr<T> instance);
template<class T>
std::shared_ptr<T> load(std::string id);
template<class Stored, class Retrieved>
std::shared_ptr<Retrieved> load_as(std::string id) {
auto stored = load<Stored>(id);
return std::static_pointer_cast<Retrieved>(stored);
}
}
This makes a messy usage at the caller site, but the information must come from somewhere:
auto shere_khan = make_shared<Tiger>();
session.store("tigers/1", shere_khan);
auto bagheera = session.load_as<Tiger, IJumpable>("tigers/1");
Solution courtesy of my brother who happens to be a C++ expert with no stackoverflow :)
Here is a void_ptr implementation that enables polymorphic casting using exception handling to discover types. The performance should be close to that of a dynamic_cast. You should be able to optimize the above using std::type_index and caching the offsets.
#include <stdio.h>
class void_ptr {
void* obj;
void (*discover_type)(void*);
template<typename T>
static void throw_typed_object(void* obj)
{
T* t = static_cast<T*>(obj);
throw t;
}
public:
void_ptr() : obj(0) {}
template<typename T>
void_ptr(T* t) : obj(t), discover_type(throw_typed_object<T>)
{
}
template<typename T>
T* cast() const
{
try {
discover_type(obj);
} catch(T* t) {
return t;
} catch(...) {
}
return 0;
}
};
struct Animal {
virtual ~Animal() {}
virtual const char* name() { return "Animal"; }
};
struct Speaker {
virtual ~Speaker() {}
virtual const char* speak() { return "hello"; }
};
struct Lion : public Animal, public Speaker {
virtual const char* name() { return "Lion"; }
virtual const char* speak() { return "Roar"; }
};
int main()
{
void_ptr ptr(new Lion());
Animal* a = ptr.cast<Animal>();
Speaker* s = ptr.cast<Speaker>();
printf("%s\n", a->name());
printf("%s\n", s->speak());
}
IMO best solution is not to cast to pointer to void but to other type which can be dynamic sidecast to required type.
#include <iostream>
#include <memory>
struct Animal
{
virtual ~Animal() {}
virtual void Pet() const = 0;
};
struct IJumpable
{
virtual ~IJumpable() {}
virtual void Jump() const = 0;
};
struct IStrorable
{
virtual ~IStrorable() {}
};
struct Tiger : Animal, IJumpable, IStrorable
{
void Pet() const override
{
std::cout << "Pet\n";
}
void Jump() const override
{
std::cout << "Jump\n";
}
};
int main()
{
auto cat = std::make_shared<Tiger>();
auto any_ptr = std::static_pointer_cast<IStrorable>(cat);
auto namable = std::dynamic_pointer_cast<IJumpable>(any_ptr);
namable->Jump();
std::cout << std::endl;
}
Live example
Other solutions require use of std::any, but this will be less handy.
It is a bit disturbing that your method load is a template.
I have the goal to make a base class, and a CRTP subbase class containing a static vector that will hold different values for each Derived class. However each object from the derived class must be able to see only one vector for the entire class. Moreover, I need to manipulate this vector through a common interface, this is why I am setting up a Base class.
Base class and subbase
class Seq {
public:
virtual unsigned long int elem(int i) = 0;
virtual void print(ostream& os) =0; // print out to a ostream
virtual int length() const =0;// return size of vector
virtual ~Seq() {}
protected:
virtual void gen_elems(int i) = 0; //generates elements
};
template<class T>
class subSeq: public Seq {
public:
unsigned long int elem(int i);
void print(ostream& os);
int length() const {return (int)memory.size();}
virtual ~subSeq() {}
protected:
static vector<long int> memory;
virtual void gen_elems(int i) = 0;
};
template<class T>
void subSeq<T>::print(ostream& os) {
if((int)memory.size() != 0) {
cout << "Stored numbers: ";
for(int i=0; i<(int)memory.size(); i++) {
os << memory[i] << " ";
}
cout << "\n";
} else {
cout << "Empty class!!\n";
}
}
template<class T>
unsigned long int subSeq<T>::elem(int i) {
if( i>=(int)memory.size() ) gen_elems(i);
return memory[i];
}
One of my derived classes:
class Fibonnacci: public subSeq<Fibonnacci> {
public:
Fibonnacci(int=0);
~Fibonnacci() {}
protected:
void gen_elems(int i); // Gera os elementos da série até o iésimo elemento (protected)
};
and its implementation:
Fibonnacci::Fibonnacci(int param) { if(param) gen_elems(param); }
void Fibonnacci::gen_elems(int param) {
for(int i=(int)memory.size(); i<param; i++) {
if(i>1) memory.push_back((long int)memory[i-1]+memory[i-2]);
else memory.push_back(1);
}
}
the problem occurs around this line
if(i>1) memory.push_back((long int)memory[i-1]+memory[i-2]);
compiler yells at me for
undefined reference to `subSeq<Fibonnacci>::memory'
this has been going on for hours, and since I am new to the concept CRTP, I see I don't understand it well and need help of people more capable than me.
Could someone enlighten me to what the problem is?
The member
template <>
vector<long int> subSeq<Fibonnacci>::memory;
Should be defined somewhere. To achieve your desired result, you should do this explicitly yourself in only a single translation unit (cpp file).
Or alternatively;
template <class T>
vector<long int> subSeq<T>::memory = {};
As some compilers do support common data folding (e.g COMDAT in msvc) which may help if you wish to use implicit instantiations, note this answer for more detail on common symbols.
I'm having a problem with inheriting from STL set (i think):
Here is class Prime:
class Prime : public set<A> {
private:
// private data members.
public:
// C'tor...
void printParticularA(const int& id);
}
Here is class A:
class A : public List<B>{
private:
// data members.
int id;
public:
// C'tor
A(const A& copy) : List<B>(copy), //copy data members
{ // validate data and throw exceptions if needed. };
bool operator< (const A& rhs) const{
return id < rhs.id;
}
void printReport() const {
for(const B& item : *this){ item.print(); }
}
}
now here is the problem. in the next function i want to print a particular A object in the set:
void Prime::printParticularA(const int& id) {
find(AFinder(id))->printReport();
}
i also tried this:
void Prime::printParticularA(const int& id) {
*(find(AFinder(id))).printReport();
}
note: assume that class B has print() method.
note2: AFinder is a class for making dummy A objects using only the id data.
the problem is that when 'find' finds the objects it returns const_iterator (because every object in set is const), and when i dereference it i get a copy of the object (??) but the list of B inside it is empty!
this happens also for the '->' version.
now i know that set doesn't allow me to change the objects but i do not intend to change the object (as you can see in the declaration of printReport member function).
i appreciate any help in this!
EDIT: thanks everyone, you have helped me a lot especially learning what not to do.
I solved the problem and it wasn't in set, list nor any of my classes presented here.
my mistake was in understanding the question i was given (yes this is my homework assignment, i'm still new to c++).
sorry if you feel i have wasted your time.
i hope that i can learn from all of your experience and someday help others!
in short, THANKS!! :)
Your code is pretty much messed up all around. And the problem does not look tied directly to set or iterator, but general bad things done.
For starters make your op< const and delete the copy ctor for good -- the stock one shall work fine. Use internal find of set to search and look if it found an item.
All that will likely make your described problem gone, if not, post a complete compileable example with proper text of what you see and what you expect.
Your code really violate many rules of C++. Why don't you try smth like this:
#include <iostream>
#include <list>
#include <map>
using namespace std;
class SimpleInt {
public:
int data_;
SimpleInt(const int data): data_(data) {};
void print() const {cout << data_ << " ";};
};
template <typename T>
class A {
private:
// private data members.
public:
list<T> list_of_B_; // this is for simlicity. Make getters as you need
const int id_; // delete it or copy to map. You sholdn't change it.
A(int id) : list_of_B_(), id_(id) {}
A(const A<T>& copy) : list_of_B_(copy.list_of_B_), id_(copy.id_) {} //copy data members
A(A<T>&& copy) : list_of_B_(::std::move(copy.list_of_B_)), id_(copy.id_) {} //move data members
void printReport() const {
for(const T& item : list_of_B_){ item.print(); }
}
};
template <typename T>
class Prime {
private:
// private data members.
public:
// The main difference with your source
map<int, T> map_of_A_; // this is for simlicity. Make getters as you need
// C'tor...
void printParticularA(const int& id) {
auto it = map_of_A_.find(id);
if (it != map_of_A_.end())
it->second.printReport();
}
};
int _tmain(int argc, _TCHAR* argv[])
{
typedef A<SimpleInt> ASimpled;
Prime<ASimpled> prime;
ASimpled a(1);
a.list_of_B_.push_back(SimleInt(1));
a.list_of_B_.push_back(SimleInt(2));
a.list_of_B_.push_back(SimleInt(3));
ASimpled b(2);
b.list_of_B_.push_back(SimleInt(10));
b.list_of_B_.push_back(SimleInt(20));
b.list_of_B_.push_back(SimleInt(30));
prime.map_of_A_.insert(make_pair(a.id_, a));
prime.map_of_A_.insert(make_pair(b.id_, b));
prime.printParticularA(2);
return 0;
}
Although you haven't included the implementation of List the problem is likely there. To be more precise the begin() and end() member functions of List may be broken. Chances are the values they return are identical (or invalid) resulting in the range based for loop doing nothing. This of course is based on your set::find is returning a valid iterator and not the end iterator.
The following example is a modification of the code in your question. It uses std::list instead of List and doesn't use AFinder since you haven't included the code for it.
#include <set>
#include <list>
#include <iostream>
struct B
{
int id_;
explicit B(int id) : id_(id) {}
void print() const
{
std::cout << "B::id = " << id_ << std::endl;
}
};
class A : public std::list<B>
{
public:
explicit A(int id) : id_(id) {}
bool operator<(const A& rhs) const
{
return id_ < rhs.id_;
}
bool operator==(const A& other) const
{
return id_ == other.id_;
}
void printReport() const
{
for(auto& item : *this)
{
item.print();
}
}
private:
// data members.
int id_;
};
class Prime : public std::set<A>
{
public:
void printParticularA(const int& id)
{
std::cout << "finding " << id << std::endl;
auto el = find(A(id));
if(el == cend())
{
std::cout << "not found" << std::endl;
}
else
{
find(A(id))->printReport();
}
std::cout << "done finding " << id << std::endl;
}
};
int main()
{
Prime p;
A a1(1);
a1.push_back(B(1));
a1.push_back(B(2));
a1.push_back(B(3));
p.insert(a1);
A a2(2);
a2.push_back(B(4));
a2.push_back(B(5));
a2.push_back(B(6));
p.insert(a2);
p.printParticularA(1);
p.printParticularA(2);
// doesn't exit
p.printParticularA(3);
}
This produces the following output.
finding 1
B::id = 1
B::id = 2
B::id = 3
done finding 1
finding 2
B::id = 4
B::id = 5
B::id = 6
done finding 2
finding 3
not found
done finding 3
I have implemented Decorator pattern in C++ as follows:
#include <iostream>
#include <string>
#include <deque>
using namespace std;
// Abstract Component
template <class T>
class IArray
{
public:
virtual void insert(const T&) = 0;
virtual ~IArray(){}
};
// Concrete Component
template <class T>
class Array : public IArray<T>
{
public:
virtual void insert(const T& elem)
{
m_array.push_back(elem);
}
private:
deque<T> m_array;
};
// Decorator 1
template <class T>
class PositiveArray : public IArray<T>
{
public:
PositiveArray(IArray<T>* component):m_component(component)
{
}
virtual void insert(const T& elem)
{
if (elem > 0)
{
m_component->insert(elem);
}
else
{
cerr << "You can't insert non-positive number." <<endl;
}
}
private:
IArray<T>* m_component;
};
// Decorator 2
template <class T>
class PrintArray : public IArray<T>
{
public:
PrintArray(IArray<T>* component):m_component(component)
{
}
virtual void insert(const T& elem)
{
m_component->insert(elem);
cout << "Element " << elem << " was inserted into the array." <<endl;
}
private:
IArray<T>* m_component;
};
// Client
int main()
{
typedef int MyType;
PositiveArray<MyType> arr(new PrintArray<MyType>(new Array<MyType>));
arr.insert(10);
arr.insert(-10);
int i;
cin>>i;
return 0;
}
Now I want to have for all arrays printArray function. Should I write it as a pure virtual function in IArray and copy the following implementation of that function in each child of IArray?
void printArray()
{
for (int i = 0; i < m_array.size(); ++i)
{
cout << "elem " <<i << " is " << m_array[i] <<endl;
}
}
Is there any solution that can avoid of copying?
I would implement for_each_element in either Array, and expose the interface in IArray. It has 2 overloads that take std::function< void(T const&) > and std::function< void(T) > (second one is optional). Now PrintArray is a one line lambda function.
In C++03 you can use boost::function, and PrintArray is more annoying to write. So here it is less tempting.
As another approach, expose const_iterators to the underlying data.
As an aside, deque performance is surprisingly poor. As yet, there is nothing in your code that would make me think you could not use a std::vector. If you guaranteed memory contiguity, you could even have your const_iterators be T const* and expose the interface directly from IArray (with the implementation in Array). for_each_element becomes a two-liner in C++11, and PrintArray even without C++11 or for_each_element is 2 lines, and either implemented inline in IArray or as a free function.
Oh, and I'd make PrintArray a free function rather than a member function. for_each_element may need to be a member function, but you should be able to PrintArray without access to private data, once you expose iterators and/or for_each_element.
I realize that I'll most likely get a lot of "you shouldn't do that because..." answers and they are most welcome and I'll probably totally agree with your reasoning, but I'm curious as to whether this is possible (as I envision it).
Is it possible to define a type of dynamic/generic object in C++ where I can dynamically create properties that are stored and retrieved in a key/value type of system? Example:
MyType myObject;
std::string myStr("string1");
myObject.somethingIJustMadeUp = myStr;
Note that obviously, somethingIJustMadeUp is not actually a defined member of MyType but it would be defined dynamically. Then later I could do something like:
if(myObject.somethingIJustMadeUp != NULL);
or
if(myObject["somethingIJustMadeUp"]);
Believe me, I realize just how terrible this is, but I'm still curious as to whether it's possible and if it can be done in a way that minimizes it's terrible-ness.
C++Script is what you want!
Example:
#include <cppscript>
var script_main(var args)
{
var x = object();
x["abc"] = 10;
writeln(x["abc"]);
return 0;
}
and it's a valid C++.
You can do something very similar with std::map:
std::map<std::string, std::string> myObject;
myObject["somethingIJustMadeUp"] = myStr;
Now if you want generic value types, then you can use boost::any as:
std::map<std::string, boost::any> myObject;
myObject["somethingIJustMadeUp"] = myStr;
And you can also check if a value exists or not:
if(myObject.find ("somethingIJustMadeUp") != myObject.end())
std::cout << "Exists" << std::endl;
If you use boost::any, then you can know the actual type of value it holds, by calling .type() as:
if (myObject.find("Xyz") != myObject.end())
{
if(myObject["Xyz"].type() == typeid(std::string))
{
std::string value = boost::any_cast<std::string>(myObject["Xyz"]);
std::cout <<"Stored value is string = " << value << std::endl;
}
}
This also shows how you can use boost::any_cast to get the value stored in object of boost::any type.
This can be a solution, using RTTI polymorphism
#include <map>
#include <memory>
#include <iostream>
#include <stdexcept>
namespace dynamic
{
template<class T, class E>
T& enforce(T& z, const E& e)
{ if(!z) throw e; return z; }
template<class T, class E>
const T& enforce(const T& z, const E& e)
{ if(!z) throw e; return z; }
template<class Derived>
class interface;
class aggregate;
//polymorphic uncopyable unmovable
class property
{
public:
property() :pagg() {}
property(const property&) =delete;
property& operator=(const property&) =delete;
virtual ~property() {} //just make it polymorphic
template<class Interface>
operator Interface*() const
{
if(!pagg) return 0;
return *pagg; //let the aggregate do the magic!
}
aggregate* get_aggregate() const { return pagg; }
private:
template<class Derived>
friend class interface;
friend class aggregate;
static unsigned gen_id()
{
static unsigned x=0;
return enforce(++x,std::overflow_error("too many ids"));
}
template<class T>
static unsigned id_of()
{ static unsigned z = gen_id(); return z; }
aggregate* pagg;
};
template<class Derived>
class interface: public property
{
public:
interface() {}
virtual ~interface() {}
unsigned id() const { return property::id_of<Derived>(); }
};
//sealed movable
class aggregate
{
public:
aggregate() {}
aggregate(const aggregate&) = delete;
aggregate& operator=(const aggregate&) = delete;
aggregate(aggregate&& s) :m(std::move(s.m)) {}
aggregate& operator=(aggregate&& s)
{ if(this!=&s) { m.clear(); std::swap(m, s.m); } return *this; }
template<class Interface>
aggregate& add_interface(interface<Interface>* pi)
{
m[pi->id()] = std::unique_ptr<property>(pi);
static_cast<property*>(pi)->pagg = this;
return *this;
}
template<class Inteface>
aggregate& remove_interface()
{ m.erase[property::id_of<Inteface>()]; return *this; }
void clear() { m.clear(); }
bool empty() const { return m.empty(); }
explicit operator bool() const { return empty(); }
template<class Interface>
operator Interface*() const
{
auto i = m.find(property::id_of<Interface>());
if(i==m.end()) return nullptr;
return dynamic_cast<Interface*>(i->second.get());
}
template<class Interface>
friend aggregate& operator<<(aggregate& s, interface<Interface>* pi)
{ return s.add_interface(pi); }
private:
typedef std::map<unsigned, std::unique_ptr<property> > map_t;
map_t m;
};
}
/// this is a sample on how it can workout
class interface_A: public dynamic::interface<interface_A>
{
public:
virtual void methodA1() =0;
virtual void methodA2() =0;
};
class impl_A1: public interface_A
{
public:
impl_A1() { std::cout<<"creating impl_A1["<<this<<"]"<<std::endl; }
virtual ~impl_A1() { std::cout<<"deleting impl_A1["<<this<<"]"<<std::endl; }
virtual void methodA1() { std::cout<<"interface_A["<<this<<"]::methodA1 on impl_A1 in aggregate "<<get_aggregate()<<std::endl; }
virtual void methodA2() { std::cout<<"interface_A["<<this<<"]::methodA2 on impl_A1 in aggregate "<<get_aggregate()<<std::endl; }
};
class impl_A2: public interface_A
{
public:
impl_A2() { std::cout<<"creating impl_A2["<<this<<"]"<<std::endl; }
virtual ~impl_A2() { std::cout<<"deleting impl_A2["<<this<<"]"<<std::endl; }
virtual void methodA1() { std::cout<<"interface_A["<<this<<"]::methodA1 on impl_A2 in aggregate "<<get_aggregate()<<std::endl; }
virtual void methodA2() { std::cout<<"interface_A["<<this<<"]::methodA2 on impl_A2 in aggregate "<<get_aggregate()<<std::endl; }
};
class interface_B: public dynamic::interface<interface_B>
{
public:
virtual void methodB1() =0;
virtual void methodB2() =0;
};
class impl_B1: public interface_B
{
public:
impl_B1() { std::cout<<"creating impl_B1["<<this<<"]"<<std::endl; }
virtual ~impl_B1() { std::cout<<"deleting impl_B1["<<this<<"]"<<std::endl; }
virtual void methodB1() { std::cout<<"interface_B["<<this<<"]::methodB1 on impl_B1 in aggregate "<<get_aggregate()<<std::endl; }
virtual void methodB2() { std::cout<<"interface_B["<<this<<"]::methodB2 on impl_B1 in aggregate "<<get_aggregate()<<std::endl; }
};
class impl_B2: public interface_B
{
public:
impl_B2() { std::cout<<"creating impl_B2["<<this<<"]"<<std::endl; }
virtual ~impl_B2() { std::cout<<"deleting impl_B2["<<this<<"]"<<std::endl; }
virtual void methodB1() { std::cout<<"interface_B["<<this<<"]::methodB1 on impl_B2 in aggregate "<<get_aggregate()<<std::endl; }
virtual void methodB2() { std::cout<<"interface_B["<<this<<"]::methodB2 on impl_B2 in aggregate "<<get_aggregate()<<std::endl; }
};
int main()
{
dynamic::aggregate agg1;
agg1 << new impl_A1 << new impl_B1;
dynamic::aggregate agg2;
agg2 << new impl_A2 << new impl_B2;
interface_A* pa = 0;
interface_B* pb = 0;
pa = agg1; if(pa) { pa->methodA1(); pa->methodA2(); }
pb = *pa; if(pb) { pb->methodB1(); pb->methodB2(); }
pa = agg2; if(pa) { pa->methodA1(); pa->methodA2(); }
pb = *pa; if(pb) { pb->methodB1(); pb->methodB2(); }
agg2 = std::move(agg1);
pa = agg2; if(pa) { pa->methodA1(); pa->methodA2(); }
pb = *pa; if(pb) { pb->methodB1(); pb->methodB2(); }
return 0;
}
tested with MINGW4.6 on WinXPsp3
Yes it is terrible. :D
It had been done numerous times to different extents and success levels.
QT has Qobject from which everything related to them decends.
MFC has CObject from which eveything decends as does C++.net
I don't know if there is a way to make it less bad, I guess if you avoid multiple inheritance like the plague (which is otherwise a useful language feature) and reimplement the stdlib it would be better. But really if that is what you are after you are probably using the wrong language for the task.
Java and C# are much better suited to this style of programming.
#note if I have read your question wrong just delete this answer.
Check out Dynamic C++