C++ "triangle" (and not diamond) inheritance - c++

(I searched and read thru the Diamond- and virtual-inheritance questions here, but could not find an answer. My thinking is that this situation is a little unusual, and I am willing to entertain the ideas that my requirements are somewhat off. On the other hand, I think this should be doable in a "nice" way.)
The situation and the requirements:
I have a C++ class library over which I have no control, and which I cannot change. It defines a Window class. The Window class has a protected member (say: handle), not otherwise accessible, that derived classes are meant to use. Window defines hundreds (well, a very large number...) of methods which I don't care to re-implement by delegating (in a decorator, say).
I want to add functionality to Window, so that derived classes (which I write; say LogWindow) automatically have. An example of such functionality is the ability to snap windows to each other. In order to implement this I need access to Window's protected handle member.
For my real-life purpose this is enough, and the solution is simple: derive SnappableWindow from Window, and derive all my Window-derived classes (LogWindow in this example) from SnappableWindow.
However, what I would really want, and is prettier IMHO, is:
The ability to have this "Snappable" functionality as a standalone piece of code, which I can choose to "plug into" any other Window-derived class, or not.
The ability to extend this notion to other functionalities as well, for example the ability to minimize windows. So I could have a Window-derived class, with or without the "Snappable" ability, and with or without the "Minimizable" ability.
The implementations of "SnappableWindow" and "MinimizableWindow" both need access to Window's handle protected member.
I would like "Snappable" and "Minimizable" to be part of the actual class declaration, so that my actual class (LogWindow) "is a" Window, "is a" SnappableWindow, and "is a" MinimizableWindow.
... and now to the question:
I get how I can do this with declaring SnappableWindow and MinimizableWindow as not deriving from Window but rather getting a handle in their constructor, and then deriving LogWindow from Window and from any combination of SnappableWindow and MinimizableWindow.
EDIT: handle is initialized, in Window, half-way thru LogWindow's constructor, after it has called Window's init(). (and not half way thru Window's constructor, as I've said before).
However, since handle is only initialized half way thru LogWindow's constructor (after it has called Window's init()) , I can't pass is to SnappableWindow and MinimizableWindow as part of LogWindow's constructor initialization list. Rather, I would have to explicitly call some init() method on both, passing it the handle. And this, in each of my Window-derived classes. (LogWindow, SearchWindow, PreferencesWindow, etc.)
I am looking for a way to be able to do something like:
class LogWindow : public Window, public SnappableWindow, public MinimizableWindow
... and not have to implement anything else inside LogWindow. I've fiddled with virtual inheritance, but can't quite come up with the solution.

Virtual inheritance should be able to handle this:
class SnappableWindow: virtual public Window
class MinimizableWindow: virtual public Window
class LogWindow: virtual public Window, public SnappableWindow, public MinimizableWindow
Note that a triangle is just a special case of a diamond!
Window
| \ \---------------\
| \ \
| SnappableWindow MinimizableWindow
| / /
| / /-------------/
LogWindow
Edit: here's a full example:
#include <iostream>
int get_handle() { static int handle = 0; return ++handle; }
struct Window {
int handle;
Window() : handle(get_handle()) { }
};
struct SnappableWindow: virtual public Window {
SnappableWindow() { std::cout << "Snap! " << handle << std::endl; }
};
struct MinimizableWindow: virtual public Window {
MinimizableWindow() { std::cout << "Mm! " << handle << std::endl; }
};
struct LogWindow: virtual public Window, public SnappableWindow, public MinimizableWindow {
LogWindow() { std::cout << "Log! " << handle << std::endl; }
};
int main() {
LogWindow lw;
std::cout << "lw: " << lw.handle << std::endl;
}
Output:
Snap! 1
Mm! 1
Log! 1
lw: 1

You could use traits for that, but unfortunately they can't access protected members. You can do it if you create an intermediate class that exposes the protected member. See if it makes sense:
struct Window {
protected:
int handle;
};
struct BaseWindow : public Window {
int get_handle() { return handle; }
};
template <class TWindow>
struct Snappable {
Snappable() { std::cout << "Snappable " << self()->get_handle() << std::endl; }
private:
TWindow *const self() {
return static_cast<TWindow*>(this);
}
};
template <class TWindow>
struct Minimizable {
Minimizable() { std::cout << "Minimizable " << self()->get_handle() << std::endl; }
private:
TWindow *const self() {
return static_cast<TWindow*>(this);
}
};
struct LogWindow: public BaseWindow, public Snappable<LogWindow>, public Minimizable<LogWindow> {
};
Look here for an interesting architectural style that uses traits.

This is actually a bit confusing... If the handle is initialized as part of the Window constructor, then it will be available after Window constructor completes in the initializer list:
class Window {
protected:
int handle;
Window() { handle = 5; }
};
struct Snappable {
int hdl;
Snappable( int handle ) : handle(handle) {}
};
struct MyWindow : Window, Snappable { // Order matters here!
MyWindow() : Window(), Snappable( handle ) {}
};
int main() {
MyWindow w;
std::cout << w.hdl << std::endl; // 5
}
It is important to note that the order of execution of the base constructors is that of the declaration in the class definition, not the sequence in the initializer list.
That being said, whether this is a good design or not is a different question.

Related

Virtual inheritance constructor arguments

I know that virtual inheritance enters into the realm of "perhaps you should be doing something different," but I sometimes it is unavoidable. I am a little confused about the preferred way to handle constructor arguments when using virtual inheritance to solve some sort of diamond problem.
Here is a very simple example to help me demonstrate:
#include <iostream>
class Ball {
public:
auto diameter() const { return _diameter; }
protected:
Ball(unsigned int diameter): _diameter(diameter) {}
private:
unsigned int _diameter;
};
class ColoredBall : virtual public Ball {
public:
enum class Color {
RED,
GREEN,
BLUE
};
auto color() const { return _color; }
protected:
ColoredBall(unsigned int diameter, Color color):
Ball(diameter),
_color(color)
{
}
private:
Color _color;
};
std::ostream& operator<<(std::ostream& os, const ColoredBall::Color& v) {
switch(v) {
case ColoredBall::Color::RED:
os << "RED";
break;
case ColoredBall::Color::GREEN:
os << "GREEN";
break;
case ColoredBall::Color::BLUE:
os << "BLUE";
break;
}
return os;
}
class BouncyBall : virtual public Ball {
public:
auto bounciness() const { return _bounciness; }
protected:
BouncyBall(unsigned int diameter, double bounciness):
Ball(diameter),
_bounciness(bounciness)
{
}
private:
double _bounciness;
};
class MyBall : public ColoredBall, public BouncyBall {
public:
MyBall():
Ball(10),
ColoredBall(20, Color::GREEN),
BouncyBall(30, 5.5)
{
}
};
int main() {
MyBall b;
std::cout << "b.diameter() = " << b.diameter() << std::endl;
std::cout << "b.color() = " << b.color() << std::endl;
std::cout << "b.bounciness() = " << b.bounciness() << std::endl;
return 0;
}
My base class Ball's constructor requires a single argument used to initialize a member variable diameter. Thus, every Ball subclass, has a diameter variable (yes, I know this could be accomplished using virtual method as well).
Next I have two subclasses of Ball (ColoredBall and BouncyBall) which both add their own member variables (they may also add some more features). Here is where my confusion starts. Since, for example, ColoredBall "is a" Ball, in order for its constructor to be complete, it must call the (non-default) constructor of Ball and thus must provide a diameter. So, it seems logical make this an argument of ColoredBall's constructor as well and "forward" this along to Ball's constructor. The same applies for BouncyBall.
Here's the "weirdness." If I now create a subclass MyBall which is both a ColoredBall and a BouncyBall, I need to explicitly call the constructor of Ball because Ball has been inherited virtually in both ColoredBall and BouncyBall. It is this explicit call that really initializes the Ball class. This means that, even though I have to pass the diameter parameter to both the ColoredBall and BouncyBall constructors, it is not used at all. This means (as I show in my example), I could pass completely different values to these constructors and only the one passed explicitly to Ball will really matter.
While I understand why this happens, I am wondering if there is a better way to handle/express this?
EDIT
As an aside, for those familiar with Qt, I have this problem crop up quite a few times when dealing with QObjects. Sometimes, I want to create a few interfaces (e.g., classes A and B) that rely on the Qt meta-object system, thus I inherit from QObject. However, what if I want to create a class that inherits from both of these interfaces (e.e., class C), this is impossible.

Inheritance and templates instanciations with pointers to simulate "virtual data"

I have a hierarchy of classes:
class Base
{
public:
Base():a{5}{}
virtual ~Base(){};
int a;
};
class Derived : public Base
{
public:
Derived():b{10}{}
int b;
};
I then have a class template that operates on whatever type it is instanciated with:
template<typename T>
class DoStuff
{
public:
DoStuff():val{}{}
virtual ~DoStuff(){};
virtual void printDoStuff() = 0;
T getVal(){return val;};
private:
T val;
};
class DoStuffWithInt : public DoStuff<int>
{
public:
virtual void printDoStuff() override {cout << "val = " << getVal() << endl;}
};
class DoStuffWithBase : public DoStuff<Base>
{
public:
virtual void printDoStuff() {cout << "a = " << getVal().a << endl;}
};
Now I would like to have a hierarchy of class like this:
class DoStuffWithBase : public DoStuff<Base>
{
public:
virtual void printDoStuff() {printVal(); cout << "a = " << getVal().a << endl;}
};
// Wrong and will not compile, trying to make a point
class DoStuffWithDerived : public DoStuffWithBase<Derived>
{
public:
void printDoStuff() override {DoStuffWithBase::printDoStuff(); cout << "b = " << getVal().b << endl;}
};
Basically I would like to have DoStuffWithBase that operates on a base be extended so that I can reuse its functions, but the extended class DoStuffWithDerived should operate on a Derived type.
I managed to get something working by templating DoStuffWithBase with a pointer to Base and extending it:
template <class T>
static void deleteIfPointer(const T& t)
{
std::cout << "not pointer" << std::endl;
}
template <class T>
static void deleteIfPointer(T* t)
// ^
{
std::cout << "is pointer" << std::endl;
delete t;
}
template<typename T>
class DoStuff
{
public:
DoStuff():val{}{}
DoStuff(const T& value):val{value}{};
virtual ~DoStuff(){deleteIfPointer(val);}
virtual void printDoStuff() = 0;
T getVal(){return val;};
private:
T val;
};
class DoStuffWithBase : public DoStuff<Base*>
{
public:
// New base
DoStuffWithBase(): DoStuff(new Base()){}
DoStuffWithBase(Base* b) : DoStuff(b){}
virtual void printDoStuff() {printVal(); cout << "a = " << getVal()->a << endl;}
};
class DoStuffWithDerived : public DoStuffWithBase
{
public:
// New derived
DoStuffWithDerived(): DoStuffWithBase(new Derived()){}
void printDoStuff() override {DoStuffWithBase::printDoStuff(); cout << "b = " << static_cast<Derived*>(getVal())->b << endl;}
};
It works but there are several things I don't like:
The code is a lot more complicated, when 99% of the time, I won't need to extend a DoStuffWithX class, I will just use DoStuffWithInt, DoStuffWithClass, DoStuffWithAnotherClass etc... Here I had to add several constructors, a special case destructor and so on.
I have to use pointers and manage them (static_cast when needed, deletion...), all in order to avoid slicing and get the right type. Also, DoStuff::val should theorically not be null, but with a pointer there is no way I can prevent that (or atleast I don't know one). Maybe using smart pointers would help a bit here ? I am not super familiar with them.
I have to manage cases where T is a pointer and when it is not. For example, the deleteIfPointer function above, but also switching between . and -> and probably more.
Is there any simpler way to achieve what I am trying to do ? A design pattern or something else ? Am I stuck with my solution and is it somewhat good ?
Edit: I tried to implement it with std::variant as in #Tiger4Hire's answer:
class Derived : public Base
{
public:
Derived():b{10}{}
int b;
};
class Derived2 : public Base
{
public:
Derived2():c{12}{}
int c;
};
using DerivedTypes = std::variant<Derived, Derived2>;
struct VariantVisitor
{
void operator()(Derived& d)
{
d.b = 17;
}
void operator()(Derived2& d)
{
d.c = 17;
}
};
class DoStuffWithVariant : public DoStuff<DerivedTypes>
{
public:
void handleBasePart(Base& base)
{
cout << "a = " << base.a << endl;
base.a = 10;
}
virtual void printDoStuff() override
{
auto unionVal_l = getVal();
if (std::holds_alternative<Derived>(unionVal_l))
{
std::cout << "the variant holds a Derived!\n";
auto& derived_l = std::get<0>(unionVal_l);
cout << "b = " << derived_l.b << endl;
handleBasePart(derived_l);
}
else if (std::holds_alternative<Derived2>(unionVal_l))
{
std::cout << "the variant holds a Derived2!\n";
auto& derived2_l = std::get<1>(unionVal_l);
cout << "c = " << derived2_l.c << endl;
handleBasePart(derived2_l);
}
std::visit(VariantVisitor{}, unionVal_l);
}
};
What I like about it:
I don't have to use pointers.
I feel the code is less tricky, easier to understand.
What I don't like about it:
The code is all in one place and it deals with all the possible Derived types (and even the Base type) at once whereas with inheritance, classes are more specialized, you can really look at a class and directly know what it does, what it overrides etc... On the other hand one could argue that it means the algorithm is in one place instead of dispatched all over the classes hierarchy.
You can't have an abstract base class as your interface.
All in all it is a really good alternative, but I am still wondering if there is a simpler way to implement dynamic polymorphism ? Do you necessarily have to resort to (base class) pointers with dynamic polymorphism ? Are std::variant the way to go now ?
Edit2: 2 other drawbacks with variants that I didn't notice at first:
All your derived class and your base class have to be defined in the same library. Clients can't easily add a new Derived class since it would mean modifying the variant and they might not have access to it.
On the project I am working on, base classes are defined in one library, and are derived in other independant "sub" libraries. So if I try to use variant in my main library, it won't be able to access the Derived types in the sub libraries, which is a major issue.
If your base class implenting the variant (DoStuff here) has other members, when you call std::visit on the variant, you might have to also embark the needed other members of DoStuff. I think you should be able to use lambdas to capture them, but still, it's a lot less straightforward than using them directly as in the case of inheritance.
Your core problem is that you cast away your type information.
C++ will always call the right function, if it knows the correct type. This is why the pattern of pointer-to-base is almost always an anti-pattern (even though it is often taught as the "C++" way to do things).
Modern C++-style is to hold things as strongly-typed pointers, and cast them to the base pointer object, only when calling a function that takes a base-pointer as a parameter.
The standard supports this way of working by providing std::variant. Thus rather than
std::vector<Base*> my_list_of_things;
my_list_of_things.push_back(new Derived); // casting away type is bad
You start with
using DerivedTypes = std::variant<std::unique_ptr<Derived1>,
std::unique_ptr<Derived2>/*,etc*/>;
std::vector<DerivedTypes> my_list_of_things;
Now you can iterate over the list, calling a function which takes a pointer-to-base, casting away the type information only during the call.
You can also visit the members of the list, with a function (often a lambda) that knows exactly the type it is working on.
So you get the best of both worlds!
This does assume you have access to C++17 or above though, also that you are not working with code that is a library (compiled) but allows the library user to make their own classes. For example, libraries like Qt can't use this way of working.
If you don't have access to C++17, you may find curiously recursing templates fit much of what you are doing. (This is a controversial pattern though, as it is ugly and confusing)

Bringing non-virtual interfaces and multi-level inheritance together

The Non-virtual Interface idiome (NVI) is pretty self explanatory: You don't write public virtual functions, but public functions that call a private virtual implementation function, like so:
class Object{
virtual void v_load();
public:
void load(){ v_load(); }
}
This enables you, the base class author, to check and enforce pre- and post-conditions or apply other functions so the author of deriving classes can't forget about them.
Now when you are the deriving author, you may want to write a base class yourself - let's call it Pawn - that extends on the functionality of load() and therefore has to override v_load(). But now you are facing a problem:
When you override v_load(), other clients that want to derive from your class, will always overwrite that behaviour, and they can not call Pawn::v_load() because it is a private function, neither can they call Pawn::load() because it is defined as { v_load; } in Object which will of course lead to an infinite loop. Additionally, requiring them to do so could lead to mistakes when they forget that call. If I would want them to enable that, I would have to specify the acces to v_load() as protected in Object, which seems like an ugly solution as it would weaken the encapsulation of Object greatly.
You could of course still override v_load() to call a new function v_pawnLoad(), which is then overridden by clients, but that seems very error-prone as a lot of clients will probably overload the wrong function.
So, how can I design Pawn in such a way that clients can still override v_load() while keeping the ability to check pre-conditions or call other functions and (if possible) not enabling, let alone requiring clients of Object or Pawn to call the base v_load() implementation?
If your intention is to allow people to "extend" as opposed to "replace" load's behaviour, then put the code you currently have in v_load in load then call an empty v_load in the end.
Or you could just make v_load protected if you want to let people choose between "replacing" or "extending".
If you just want to allow them to replace the behaviour, your code is fine as it is.
As a bonus, in all these 3 variants you can change "allow" with "force" by making your v_load a pure virtual if you have no default behaviour.
If you wish to limit the override to your Pawn child class, add the final keyword to v_load in Pawn and use another virtual function to allow children of Pawn to customise its behaviour.
How about mixin' in some CRTP?
#include <iostream>
class BaseObject
{
private:
virtual void v_load() = 0;
public:
void load() { v_load(); }
};
template<typename Derived>
class Object : public BaseObject
{
private:
virtual void v_load() { static_cast<Derived&>(*this).load(); }
};
class Pawn : public Object<Pawn>
{
public:
void load() { std::cout << "Pawn::load()" << std::endl; }
};
class BlackPawn : public Pawn
{
private:
virtual void v_load() {
std::cout << "BlackPawn::v_load()" << std::endl;
std::cout << "- "; Pawn::load();
}
public:
void load() {
std::cout << "BlackPawn::load()" << std::endl;
std::cout << "- "; Pawn::load();
}
};
class BigBlackPawn : public BlackPawn
{
private:
virtual void v_load() {
std::cout << "BigBlackPawn::v_load()" << std::endl;
std::cout << "- "; BlackPawn::load();
}
public:
void load() {
std::cout << "BigBlackPawn::load()" << std::endl;
std::cout << "- "; BlackPawn::load();
}
};
template<typename T>
void load(T& x)
{
x.load();
}
void vload(BaseObject& x)
{
x.load();
}
int main()
{
Pawn p;
BlackPawn bp;
BigBlackPawn bbp;
load(p);
load(bp);
load(bbp);
std::cout << std::endl;
vload(p);
vload(bp);
vload(bbp);
}
Output on ideone.

Is there a better way than friend classes here?

Programming in C++ for Windows although this situation could arise anywhere. This is a simplified version of my problem to keep the question manageable so don't get too caught up on the detail :)
I have a class class Window which contains a windows HWND data item. I want to fully encapsulate that HWND so that the user of the class has to go through the class to perform any operations on that window, so it's stored in a private member variable. I don't want to provide any public "getter" for it as that would break the encapsulation allowing the user to bypass my class.
Now I want to create a class class Direct3d11 to encapsulate some of the directx api. In order to create an instance of this class it requires the HWND of a window so I pass it a Window object in it's constructor.
class Direct3D11
{
public:
Direct3D11(const Window& window);
};
Inside the constructor it has access to the window object, however it requires the HWND contained within in order to be able to physically create the windows objects that the Direct3D11 class will manage, but there is no way for it to obtain that information.
I could add a private getter function to get the HWND to the Window class, and then make the Direct3D11 class a friend class of Window so that it call call the function.
However this doesn't seem very elegant not least because class Window has otherwise no need to know anything at all about class Direct3D11.
Am I missing a better way to achieve this? Friend classes don't appeal, and having a public getter function doesn't much appeal either.
You could create the Direct3D11 class inside Window, since Windows owns the HWND.
Something along these lines:
class Window
{
HWND hwnd;
Direct3D11 d;
public:
Window() : d(hwnd) {}
Direct3D11& getDirect3D()
{
return d;
}
}
In your case I suggest to provide a getter for the HWND because you will probably be needing that more often. Providing the getter does not mean that you take the responsibility of your Window class, it is still responsible for the window's life cycle. You just make it more usable and easier to divide your code in use cases.
That said, here is a more generic approach that you could try:
class Window;
class Direct3D {
public:
void apply(Window &window, HWND hWnd);
};
class Window {
public:
void accept(Direct3D &direct3d) {
direct3d.apply(*this, this->m_hWnd);
}
};
You could maybe have a function on Window called Execute. It would take in a std::function with a placeholder for HWND as a parameter. Window would then call the function with HWND as its only parameter.
This would require c++11, but the code would be similar to :
#include <functional>
#include <iostream>
struct Foo {
explicit Foo(int num) : num_(num) {}
template<typename T>
void execute(std::function<T> f) const { f(num_); }
private:
int num_;
};
struct Bar{
void print_nums(int i,int j)
{
std::cout << "i:" << i << ", " << "j:" << j << std::endl;
}
};
int main()
{
Foo o(42);
Bar b;
//the function we want to execute requires an int
//that Foo knows about
typedef void myFunction(int);
// store the result of a call to std::bind
std::function<myFunction> display_1337_first = std::bind(&Bar::print_nums, b,1337, std::placeholders::_1);
std::function<myFunction> display_1337_last = std::bind(&Bar::print_nums, b, std::placeholders::_1, 1337);
o.execute<myFunction>(display_1337_first);
o.execute<myFunction>(display_1337_last);
return 0;
}
//output:
//i:1337, j:42
//i:42, j:1337
If you are willing to use the friend keyword you can make sure window has no knowledge of the class that needs the hwnd. Just make classes (that window and DirectX inherit from) that handle the actions for you. This allows you to solve the problem for DirectX, AND for the next time it comes around.
Side Rant:
Friend is not a four-letter word. Friend, if used reasonably, is actually a great way to add gradation to C++'s access control (public, friend (when in protected), protected, friend (when in private) , private).
#include <iostream>
class HwndOwner;
class HwndWanter
{
protected:
HwndWanter(){}
int getHwndFromOwner(HwndOwner & owner);
};
class HwndOwner
{
protected:
HwndOwner() : hwnd(42){}
private:
friend class HwndWanter;
int getHwnd()
{
return hwnd;
}
int hwnd;
};
class Window : public HwndOwner
{
//This is not the class you are looking for...
};
class Direct3D : private HwndWanter
{
public:
Direct3D(HwndOwner & owner)
: HwndWanter()
{
std::cout << getHwndFromOwner(owner) << std::endl;
}
};
int HwndWanter::getHwndFromOwner(HwndOwner & owner)
{
return owner.getHwnd();
}
int main()
{
Window window;
Direct3D hwndWanter(window);
}
Output:
42

Is there a better way to handle assigning identities to classes within a hierarchy for runtime use?

I'm trying to make a sanely-usable implementation of my events system. In order to identify event types,
I have what I call a "type path,' which identifies the path to an event type through the hierarchy.
This way, I can handle, for example, all InputEvents at one place whether they're key presses, mouse input,
or whatever else. A sticky issue, though, has been giving event types their identities. What I've most
recently done is do this by having each instance retrieve a leaf identity from a static member function
of the Event class, which serves simply as an interface other than performing this function.
However, the simplest way to ensure that each type has exactly one identity within this structure
seemed to be to use maps based on type paths (up to but excluding the leaf identity/identifier)
and typeid().hash_code().
Specifically, what I want to have is a system to which events can be added easily without
having to look up a bunch of information or perform a lot of silly boilerplate crap. Considering this
(and possibly things I'm not realizing I should want?),
Is this design flawed in any obvious ways?
Is there a Betterâ„¢ way than to use typeid()? I've read a bit about it and it seems to be considered something that, depending on the person whose opinion is being asked, should either never be used or be used almost never. As it's possible that I'm just rusty or being stupid, I'd like to know if anyone knows of a solution that is, if nothing else, less uncertain (apparently some implementations of typeid() are pretty bad, though I don't know which or if it's bad enough to seriously matter).
Fairly simple example of what I have now:
#include <iostream>
#include <vector>
#include <typeinfo>
#include <map>
void spew(std::vector<unsigned int> vect) { for (unsigned int i=0;i<vect.size();++i) std::cout << vect.at(i) << ","; std::cout << std::endl; }
class Foo
{
public:
Foo() {}
virtual ~Foo() {}
static unsigned int getSubtype(std::vector<unsigned int> typePath, Foo *evt)
{
static std::map<std::vector<unsigned int>, std::map<std::size_t, unsigned int> > typeMap;
std::size_t typehash = typeid(*evt).hash_code();
if (typeMap.find(typePath) == typeMap.end())
{
unsigned int val = typeMap[typePath].size();
typeMap[typePath][typehash] = val;
return val;
}
else
{
if (typeMap[typePath].find(typehash) == typeMap[typePath].end())
{
unsigned int val = typeMap[typePath].size();
typeMap[typePath][typehash] = val;
return val;
}
return typeMap[typePath][typehash];
}
}
virtual void test() { std::cout << "Foo" << std::endl; }
protected:
std::vector<unsigned int> m_typePath;
};
class Bar : public Foo
{
public:
Bar()
{
m_typePath.push_back(Foo::getSubtype(m_typePath, this));
test();
}
virtual ~Bar() {}
virtual void test() { std::cout << "Bar: "; spew(m_typePath);}
};
class Baz : public Foo
{
public:
Baz()
{
m_typePath.push_back(Foo::getSubtype(m_typePath, this));
test();
}
virtual ~Baz() {}
virtual void test() { std::cout << "Baz: "; spew(m_typePath);}
};
class Qux : public Baz
{
public:
Qux()
{
m_typePath.push_back(Foo::getSubtype(m_typePath, this));
test();
}
virtual ~Qux() {}
virtual void test() { std::cout << "Qux: "; spew(m_typePath);}
};
int main()
{
Foo foo0;
std::cout << "----" << std::endl;
Bar bar0;
std::cout << "----" << std::endl;
Baz baz0;
std::cout << "----" << std::endl;
Qux qux0;
}
Output:
----
Bar: 0,
----
Baz: 1,
----
Baz: 1,
Qux: 1,0,
This and other tests exhibit the desired behavior, to be clear.
Edit: Previous title didn't really match what I mean to ask.
Possibly relevant notes: This is meant for part of a library, and a highly parallel one at that. I've omitted code relevant to concurrency for simplicity of representing the design, but it may be that such information would be useful for design purposes as well. Also note that I'm still only asking for help with creating/assigning type identifiers; I mention these because some designs may not be applicable given their implied constraints.
Win edit:
Well, I have an implementation that's ridiculously fast and does exactly what I need. With a few derived classes, I can instantiate ten million per ~thread(I added in TBB for some other tests; it may or may not use exactly eight threads however it pleases) spread across the derived classes, each having two or more elements in its path, in typically well under .02s. Original implementation managed about four or five seconds depending on containers and such and was just silly. Result (enough to get the idea, anyway):
template<typename T> class EventID
{
public:
static const std::size_t typeID;
};
template<typename T> const std::size_t EventID<T>::typeID = typeid(T).hash_code();
class Foo
{
public:
Foo()
{
m_typePath.push_back(EventID<Foo>::typeID);
}
protected:
neolib::vecarray<std::size_t, 100, neolib::nocheck> m_typePath;
};
class Bar : public Foo
{
public:
Bar()
{
m_typePath.push_back(EventID<Bar>::typeID);
}
};
I would rely on the class hierarchy that the compiler maintains, plus a list of type codes:
typedef enum { EVENT, INPUT, MOUSE, MOUSEDOWN, MOUSEUP, MOUSEMOVE, KEYBOARD, KEYDOWN, KEYUP } EventType;
typedef std::vector<EventType> ETVector;
class Event
{
public:
virtual void appendType(ETVector& v) { v.push_back(EVENT); }
};
class InputEvent : public Event
{
public:
virtual void appendType(ETVector& v) { v.push_back(INPUT); Event::appendType(v); }
};
class MouseEvent : public InputEvent
{
public:
virtual void appendType(ETVector& v) { v.push_back(MOUSE); InputEvent::appendType(v); }
};
class MouseDownEvent : public MouseEvent
{
public:
virtual void appendType(ETVector& v) { v.push_back(MOUSEDOWN); MouseEvent::appendType(v); }
};
class MouseUpEvent : public MouseEvent
{
public:
virtual void appendType(ETVector& v) { v.push_back(MOUSEUP); MouseEvent::appendType(v); }
};
class MouseMoveEvent : public MouseEvent
// . . .
class KeyboardEvent : public InputEvent
// . . .
class KeyDownEvent : public KeyboardEvent
// . . .
class KeyUpEvent : public KeyboardEvent
// . . .
Then to do your test, you would have something like this:
KeyUpEvent kue;
EventTypeVector type_path;
kue.appendType(type_path);
for (EventTypeVector::const_iterator i = type_path.begin(); i != type_path.end(); i++)
{
cout << *i << endl;
}
The vector stores your type path. There is no runtime storage cost, and no static variables. It might be possible to use typeid instead of a manually maintained enum, but with an enum everything is under your control, and you can easily avoid conflicts with other types in your program. Alternatively, it might be possible to imbue a class with the appendType() method by means of a template, to reduce the code even further.
It is a little tedious to have to explicitly name the parent class in appendType(), but I know of no other way in C++, thanks to multiple inheritance. In Java I could have used super, though reflection would probably be a better approach.
Is this simpler than what you have, or am I missing something? Good luck.
See final edit to original post for solution.