0

I hope this is not overly convoluted. I have tried searching through the internet for a while and while I've found a lot of Q/As dealing with templates, inheritance, etc., none of the resources I found dealt with this specific issue:

I'd like to create a wrapper for std::vector<T> which will only create vectors of type std::vector<MyClass> or any of its inherited classes.

This would basically look like:

Class Base;
Class Derived : Base;

template <Base T> class myVector{
public:
    void add(T);
protected: 
    std::vector<T> list;
}

The goal is to be able to have a library that will have the following behaviour:

Base       A;
Derived    D;
int        i;    

myVector<A> AVector;  // This should work
myVector<D> DVector;  // This should work
myVector<i> iVector;  // This should NOT work

AVector.add(A);  // This should work
AVector.add(D);  // This should work (through polymorphism)

DVector.add(D);  // This should work
DVector.add(A);  // This should not work

Can anyone comment on the above question/examples? My ultimate goal is to roll my own basic event handling in c++. In this case, A would be the analogue of c# EventArgs. B would allow for specific EventArgs type. I have no doubt that Boost et al have similar functionality, but I'd like to steer clear of that route for the time being.

In practice the code will work something like (based somewhat on a tutorial that I found somewhere out there on the internet):

template<EventArgs arg> class Event{
    typedef void (*fptr)(arg);
    std::vector<fptr> listeners;
}


void notifylisteners(arg){
for (typename std::vector<fptr>::iterator iter = listeners.begin(); iter != listeners.end; ++iter)
    (**iter)(arg);  // there should be a check for a valid reference, in case one of the function pointers becomes invalid

The declaration

Event<myEventHandlerType> myEvent; 

would effectively function identically to the C# code:

public event myEventHandlerType myEvent;

Templates are required for this implementation due to the reliance on std::vector. The benefit of using a non-type parameter template here (if it works) is that it would force the use of a specific eventArg parameter type to be passed to all registered handlers, and that errors would fail during compilation.

Any thoughts or comments would be greatly appreciated.

Michael

Shmuel Levine
  • 550
  • 5
  • 18

2 Answers2

2

If you're using C++11 you have access to std::is_base_of<> and static_assert()

You can make the compiler give you an error when myVector is passed with a type that cannot be cast to Base.

#include <type_traits>

class Base
{};

class Derived : public Base
{};

template <class T>
class Foo
{
public:
    Foo()
    {
        static_assert (std::is_base_of<Base,T>::value, "Error: Type T not derived from Base");
    }
};

class Lol
{
};


Foo<Derived> foo1;
Foo<Lol> foo2; //Static assert fails

(See Compiler output here http://ideone.com/NAXSA)

If you don't have C++11 support then you have a little more legwork, but it can still be done. See here regarding that https://stackoverflow.com/a/4532302/564944

Community
  • 1
  • 1
Tocs
  • 752
  • 4
  • 17
  • I don't see a reason then why not to use features of C++11. I am using Intel compiler (latest) and/or gcc (also latest) on Arch Linux. This is mainly for myself -- portability is not a major concern for me. – Shmuel Levine Jul 17 '12 at 19:02
  • By all means, C++11 away! That note was mainly in case you were a poor soul still stuck on say VS2008, which is my current case at work. – Tocs Jul 19 '12 at 20:32
1

I hope I understood your description correctly. The C++ way of doing this would not be through inheritance, nor would you "enforce" that a type inheriting from A is passed in. Instead, use duck typing: if it looks like a duck, walks like a duck, and quacks like a duck, then it's a duck, regardless of its "official" type.

Concretely, myVector<T> would simply accept any type T. If you try to do something to a T object that it doesn't support, such as calling it with particular arg, it will fail at compile time.

Of course, it is a good idea to document the signature of the operations that T needs to support -- in essence, the interface that it implements.

(Side note: The notion of C++0x "concepts" would have allowed you to express this in code, but it was dropped from the standard.)

Thomas
  • 174,939
  • 50
  • 355
  • 478
  • The type T here would really just a wrapper class with the argument to the callback function-i.e. event handler. There really aren't any hard constraints on the class T; this would be more a matter of consistency, although I can foresee possible issues down the road with consumers of this library. If this become an issue, I understand from your comment that I can simply create some sort of dependancy that would otherwise fail - possibly by declaring a new pointer of the base type that would be assigned to value of T. – Shmuel Levine Jul 17 '12 at 18:54