C++20 Concept for checking presence of private class members - c++

Consider the situation of having some class A together with a factory class Factory. A is supposed to have only private constructors (to prevent the user from creating such an object), and is friends with Factory, who can then construct Objects of type A.
A also has some private attribute which cannot be set in in A's constructor (because the value is not known yet), but will be computed by Factory and then set correctly before A is returned.
Now i have the situation that I have similar classes B,C, ... to A that should also be constructed by Factory, which will thus be a template.
So the situation is currently
template<typename T>
class Factory {
public:
T get() {
T t;
void* computed_value = nullptr; // suppose we compute a
t._value = computed_value;
return t;
}
};
class A {
// Some public methods for the user
friend class Factory<A>;
private:
A() : _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// some more attributes that will correctly be initialsed by the default constructor
};
class B {
//Some public methods for the user
friend class Factory<B>;
private:
B(): _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// Some more attributes that will correctly be initialised by the default constructor
};
void foo() {
Factory<A> a_factory;
A a_elem = a_factory.get();
Factory<B> b_factory;
B b_elem = b_factory.get();
}
Actually, an instance of Factory<A> also stores some values that are used in the construction of some A, that are set by the constructor of Factory, this is why i use instances of the Factory and not static methods.
Now comes the point: I would like to use Concepts in order to constrain the template<typename T> that is used by the Factory, so something like:
template<typename T>
concept Factorable = requires (T t) {
std::same_as<void*, decltype(t._value)>;
};
template<Factorable t>
class Factory { //...
As expected, this does not work, because the corresponding required expressions are private and thus not available, Factorable<A> will just be false.
Now, hanging the attribute and constructor of class A to public will thus lead to a nice generic Factory and the below (correct) code
template<Factorable T>
class Factory {
public:
T get() {
T t;
void* computed_value = nullptr; // suppose we compute a
t._value = computed_value;
return t;
}
};
class A {
// Some public methods for the user
template<Factorable T>
friend class Factory;
public:
A() : _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// some more attributes that will correctly be initialsed by the default constructor
};
class B {
//Some public methods for the user
template<Factorable T>
friend class Factory;
public:
B(): _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// Some more attributes that will correctly be initialised by the default constructor
};
void use() {
Factory<A> a_factory;
A a_elem = a_factory.get();
Factory<B> b_factory;
B b_elem = b_factory.get();
}
Note the subtlety that we now had to declare all generic variants of Factory as a friend of classes A and B, since Factory<A> cannot be evaluated while defining class A itself and thus not explicitly declared as a friend. I am however fine with this, since in the implementation of the Factory<T> we will not access other classes than T anyways (It would however be nice to achieve a friend class Factory<A>, but this is not my priority).
But this approach leads to the massive problem that a user can now generate instances of A by just calling its constructor, which just sets _value = nullptr, so we get 'invalid' instances of A that may lead to undefined behaviour etc. So this is really not the way to go for, although the use of the concepts in above expression would be nice.
So my question now is:
How can I achieve both of the above, in order have private constructors and attributes, but still use concepts
I found out that concepts are actually context-sensitive, in the sense that the required expressions are checked in the invoking context, so in fact the following code will correctly compile
template<typename T>
concept Factorable = requires (T t) {
std::same_as<void*, decltype(t._value)>;
};
template<typename T>
class Factory {
static_assert(Factorable<T>);
public:
T get() {
T t;
void* computed_value = nullptr; // suppose we compute a
t._value = computed_value;
return t;
}
};
class A {
// Some public methods for the user
template<typename T>
friend class Factory;
private:
A() : _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// some more attributes that will correctly be initialsed by the default constructor
};
class B {
//Some public methods for the user
template<typename T>
friend class Factory;
private:
B(): _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// Some more attributes that will correctly be initialised by the default constructor
};
void use() {
Factory<A> a_factory;
A a_elem = a_factory.get();
Factory<B> b_factory;
B b_elem = b_factory.get();
}
since now the compiler will first accept any typename T as generic and then check the concept for the given T within the context of the class, which will thus evaluate to true, since Factory is friends with both A and thus has access to its data members.
So this approach can actually guarantee user-safe instantiation of class A and throwing corresponding errors when the static_assertion fails to evaluate, but of course this is not the way a concept is intended to use, since then we basically just fall back to duck-typing as it was before the introduction of concepts and manually assert certain things, which loses the desired properties of concepts (late error throwing etc., but also losing IDE completion).
A solution would of course be to have the possibility to require the presence of private attributes in a concept, something like the following
//Suppose the existence of a function like std::ignore_private(expression)
template<typename T>
concept Factorable = requires (T t) {
std::same_as<void*, decltype(std::ignore_private(t._value))>;
}
where std::ignore_private is of course completely made up (and i also don't know what syntax it actually should have), but you get the point what i actually want.
So, is there any way to express something like this in a concept?
At least, i could imagine that, since checking for private attributes is a constexpr and can also be checked at compile time.

If you don't find a better solution a compromise could be to constrain the method:
template<typename T>
class Factory {
public:
T get() requires Factorable<T>
{
// ...
}
};

After experimenting around a while, i came up with the following workaround, using an additional Tester class, that has to be declared friend by A:
template<typename T>
concept PrivateFactorable = requires (T t) {
std::same_as<void*, decltype(t._value)>;
};
template<typename T> class Tester {
public:
constexpr static bool factorable = PrivateFactorable<T>;
};
template<typename T>
concept Factorable = Tester<T>::factorable;
template<Factorable T>
class Factory {
public:
T get() {
T t;
void* computed_value = nullptr; // suppose we compute a meaningful value here
t._value = computed_value;
return t;
}
};
class A {
// Some public methods for the user
template<Factorable T>
friend class Factory;
friend class Tester<A>;
public:
A() : _value(nullptr) {} //initialised with nullptr since we have no meaningful value (yet)
void* _value;
// some more attributes that will correctly be initialsed by the default constructor
};
This will correctly redirect Factorable<A> into the tester class, who can evaluate the concept withing friend context with A. The only downside is that A has to have yet another friend (and we get some more concepts in the background), but this seems okay

First, I think it’s a compiler bug if the value of a concept depends on the lexical location of requesting it.
It could interfere with subsumption where that matters, but you can write the constraint directly:
template<class T> requires std::same_as<void*, decltype(T::_value)>
class Factory {…};
Substitution failures in a constraint cause it to be unsatisfied whether it’s in a concept or not.

Related

Create a templated variable within a templated class of a different type

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.

How to initialize a "bad" empty reference to abstract class

I'll get straight to it: I have a class template that holds a reference and updates info to it:
template<class T>
class Parser {
T& m_ref;
public:
typedef T ValueType;
Parser(T& ref): m_ref(ref) {}
virtual void read(std::istream&);
};
Now, I have another template that creates a new object and updates it using this interface, to do so I have a field that saves the parser.
However, I'd like to use updater to classes that derive from T, which is impossible with poymorphism since Parser<Derived> does not inherit from Parser<Base>.
I created this work-around that uses an intermediate class that inherits from Parser<Base> but updates into a Parser<Derived>:
template<class T>
struct dummy {};
template<class T>
class Creator {
typedef shared_ptr<Parser<T> > ParserPtr;
typedef shared_ptr<T> ValuePtr;
ValuePtr m_output;
ParserPtr m_parser;
template<class ParserType>
class LocalParser : public Parser<T> {
ParserType m_parser;
public:
LocalParser(typename ParserType::ValueType& val):
Parser<T>(/*???*/), //problems start here, I must initialize the base
m_parser(val) {}
void read(std::istream& is) { //use polymorphism to update into some derieved reference
m_parser.read(is);
}
};
public:
Creator(): //Uses Parser<T> as default parser
m_output(new T),
m_parser(new Parser<T>(*m_output)) {}
template<class ParserType>
Creator(dummy<ParserType>) { //Use any parser
auto temp = make_shared(new typename ParserType::ValueType);
m_output = temp;
m_parser = maked_shared(new LocalParser<ParserType>(*temp));
}
virtual ValuePtr read()(std::istream& is) {
m_parser->read(is);
return m_output;
}
};
Basically LocalParser is an intermediate class that inherits from Parser<T> but updates a diffrent reference from the one it's base holds.
The problem here is how to initialize Parser<T>, especially when T is abstract (which is 99% of the time I actually use this class with a derived parser).
My question boils down to "how to define a reference to a (possibly) abstract class that WON'T be used?" (or is there any kind of other work around where I don't define an intermidiate that inherits from Parser<T>).
EDIT: The Parser interface is a separate code I cannot change.
You cannot create an empty reference. A reference must refer to something. That's one of the key differences between a reference and a pointer. In fact, there's a possible solution for you:
T& ref; // error
T& ref = nullref; // no such thing
T* ptr = nullptr; // "empty" pointer!
Another possibly more explicit solution that allows for either a reference or nothing would be to use boost::optional:
boost::optional<T&> opt_ref; // empty optional
opt_ref = some_t;

Is it possible to resolve static members similarly to overloading the member access operator for another type?

I am not sure what to call it, but is something like this possible as the commented out line reflects?
template <typename T>
class Test
{
public:
Test(T& t) : m_t(t) {}
T* operator->() { return &m_t; }
private:
T& m_t;
};
class A
{
public:
static const int integer = 0;
void function() {}
};
int main()
{
A a;
Test<A> test(a);
test->function();
// Something similar to doing Test<A>::integer?
return 0;
}
Well, why don't you do:
test->integer;
You can always access static members the same way as non-static ones (i.e. from an instance variable).
The other option would be to define in Test:
template <typename T>
class Test
{
public:
typedef T value_type;
// ...
};
In which case you will be able to do:
Test<A>::value_type::integer;
which will avoid the need of creating an instance of Test<A>.
At last, if you are using C++11 and Test follows the smart pointers conventions, then you will have:
std::pointer_traits<Test<A> >::element_type::integer;
which has the advantage to work even if you replace Test<A> with A*.
No. In C++, "overloading" only makes sense for functions. Instead of mixing static and non-static items in a class, you could try making two separate classes, both with all non-static members. Return your value from a function call, rather than using a public static member variable.

C++11 Pure virtual 'templated' return type in non-templated base class

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).

Is there anything better than a metafactory to work around constructor injection into derived classes in CRTP?

In the CRTP, I want to inject the constructor into the derived class, cleanly - without use of macros and without writing it out. It seems it's impossible, so I've come up with some workarounds.
First, there's an underlying event class (QEvent) that should have a unique integer type tag for every derived class (see rationale). You obtain it by calling a registration function It's easy enough to create a CRTP wrapper that will hide this from you:
template <typename Derived> class EventWrapper : public QEvent {
public:
EventWrapper() : QEvent(staticType()) {}
static QEvent::Type staticType() {
static QEvent::Type type = static_cast<QEvent::Type>(registerEventType());
return type;
}
};
class MyEvent1 : public EventWrapper<MyEvent1> {}; // easy-peasy
class MyEvent2 : public EventWrapper<MyEvent2> {};
Note that MyEvent1::staticType() != MyEvent2::staticType(): registerEventType() returns unique types each time it's called.
Now I want the event class to carry some data:
template <typename Derived> class StringEvent : public EventWrapper<D> {
std::string m_str;
public:
explicit StringEvent(const std::string & str) : m_str(str) {}
std::string value() const { return m_str; }
};
But here we run into a problem: we need to manually define the constructor in each of the derived classes. The whole point here is that creation of such classes should be easy, as there may be many different string-carrying event types. But it's anything but easy:
class MyEvent3 : public StringEvent<MyEvent3> {
public: MyEvent3(std::string s) : StringEvent(s) {}
};
This obviously gets old real quick, even with C++11 constructor forwarding:
class MyEvent3 : public StringEvent<MyEvent3> { using StringEvent::StringEvent; };
What we'd want is a way of injecting this constructor into the derived class, or avoiding doing so while still providing for ease of use. Sure you can hide it in a preprocessor macro, but I hate those macros, they are a maintenance pain as they introduce new names for very simple concepts.
We can of course use a dummy type. Note that there's no need for a definition of the dummy type. It's only a name to be used as the type argument.
// Pre-C++11
class DummyEvent3;
typedef StringEvent<DummyEvent3> MyEvent3;
// C++11
class DummyEvent3;
using MyEvent3 = StringEvent<DummyEvent3>;
Another solution would be to use an int template argument and use an enum value, but this brings back the uniqueness issue that got solved by using the registerEventType() in the first place. It'd be no fun to guarantee that a large program is correct. And you'd still need to spell out the enum.
So, I've come up with a metaprogram class that I'll call a metafactory, that can produce the ready-to-use StringEvent classes for us, while keeping it all to one type definition:
// the metafactory for string events
template <typename Derived> class StringEventMF {
public:
class Event : public EventWrapper<Derived> {
std::string m_str;
public:
explicit Event(const std::string & val) : m_str(val) {}
std::string value() const { return m_str; }
};
};
or simply
template <typename Derived> class StringEventMF {
public:
typedef StringEvent<Derived> Event;
};
This is used like:
class Update : public StringEventMF<Update> {};
class Clear : public StringEventMF<Clear> {};
void test() {
Update::Event * ev = new Update::Event("foo");
...
}
The classes you use are Update::Event, Clear::Event. The Update and Clear are metafactories: they generate the desired event class for us. The derivation from the metafactory sidesteps derivation from the concrete class type. The metafactory type gives the unique type discriminator needed to create unique concrete class types.
The questions are:
Is there any "cleaner" or "more desirable" way of doing it? Ideally, the following non-working pseudocode would be my ideal way of doing it - with zero repetition:
class UpdateEvent : public StringEvent <magic>;
The name of the derived class appears only once, and the name of the base concept StringEvent appears only once, too. The CRTP requires the class name to appear twice - so far I think it's acceptable, but my metaprogramming-fu is in tatters. Again, I want a preprocessor-less solution, it'd be a no-brainer otherwise.
Is the name metafactory my original invention (ha ha), or is it merely my google-Fu that's lacking? This metafactory pattern seems to be quite flexible. It's easy to compose metafactories by multiple derivation. Say you wanted an Update::Event made by one factory, and Update::Foo made by another.
This question is motivated by this answer. Note: in real code I'd be using QString, but I'm trying to keep it as generic as possible.
I think what you're looking for might be just using placement new to instantiate the base class.
The derived class won't be constructable because unless they will create a matching constructor.
But, they don't have to be constructable, you could use them anyway. (It could still be destructable).
template <class T>
class Base
{
protected: Base(int blah) { }
public: static T* CreateInstance(int data) {
T* newOjectBlock = reinterpret_cast<T*>(::operator new(sizeof(T))); // allocate enough memory for the derived class
Base* newBasePlace = (Base*)(newOjectBlock); //point to the part that is reseved for the base class
newBasePlace= new ((char*)newBasePlace) Base(data); //call the placement new constrcutor for the base class
return newOjectBlock;
}
};
class Derived : public Base<Derived> {}
Then let the CRTP base class construct the derived class like this:
Derived* blah = Derived::CreateInstance(666);
If anyone ever wants to initialize the derived class, they should either make a matching constructor that calls the base class constructor.
OR, just make an .init() method that initiates its members, and will be called after the instance is created.
OR, we can think of something else, this is just an idea of a concept.
Yochai Timmer has come up with an alternative way of approaching the problem. Instead of having to forward the constructor from the data carrier class, he exposes a factory method that produces pseudo-derived classes. As it invokes undefined behavior, I'm not particularly keen on it.
Expanding a bit on the original metafactory concept, it's possible to make generic metafactory that can be used to make unique event types that wrap "any" data-carrying class.
The approach for C++11 uses constructor forwarding so that plain non-template data carrier classes can be used. The approach for C++98 requires a templated data carrier class and, internally, a bit more gymnastics, but it works as well.
The event classes can't be further derived from. This is necessary since the derived classes would all share the value of staticType, and that can't be allowed, as DyP duly noted in the comments.
To test the code, you need the event wrapper, the metafactory and data carrier selected for your variant of C++, and the test/usage part.
The Event Wrapper (Common Code)
In either case, our basic event wrapper CRTP class that generates a unique static type value for the event is:
// A type-identifier-generating wrapper for events. It also works with RTTI disabled.
template <typename Derived> class EventWrapper : public QEvent {
public:
EventWrapper() : QEvent(staticType()) {}
static QEvent::Type staticType() {
static QEvent::Type type = static_cast<QEvent::Type>(registerEventType());
return type;
}
static bool is(const QEvent * ev) { return ev->type() == staticType(); }
static Derived* cast(QEvent * ev) { return is(ev) ? static_cast<Derived*>(ev) : 0; }
};
Note that it also provides a cast-to-derived method. You'd use it in an event handler, given a pointer to a base event class:
void MyClass::customEvent(QEvent* event) {
if (MyEvent::is(event)) {
auto myEvent = MyEvent::cast(event);
// use myEvent to access data carrying members etc)
}
}
The C++98 Metafactory
The Carrier is a parametrized data carrier class, such as StringData below.
// The generic event metafactory
template <typename Derived, template <typename> class Carrier> class EventMF {
class EventFwd;
class Final;
class FinalWrapper : public EventWrapper<EventFwd>, public virtual Final {};
public:
// EventFwd is a class derived from Event. The EventWrapper's cast()
// will cast to a covariant return type - the derived class. That's OK.
typedef Carrier<FinalWrapper> Event;
private:
class EventFwd : public Event {};
class Final {
friend class FinalWrapper;
friend class Carrier<FinalWrapper>;
private:
Final() {}
Final(const Final &) {}
};
};
The EventFwd class is needed so that we have something sane to pass to the EventWrapper template as the derived class, so that the cast() static method will work. The FinalWrapper is there since in pre-C++11 we can't friend typecasts.
Now for the parametrized data carrier. It'd be the same as for the C++11 variant below except for needing to have a parametrized base class.
// A string carrier
template <typename Base> class StringData : public Base {
QString m_str;
public:
explicit StringData(const QString & str) : m_str(str) {}
QString value() const { return m_str; }
};
The C++11 MetaFactory
// The generic metafactory for unique event types that carry data
template <typename Derived, class Data> class EventMF {
class Final;
EventMF();
EventMF(const EventMF &);
~EventMF();
public:
class Event : public EventWrapper<Event>, public Data, private virtual Final {
public:
template<typename... Args>
Event(Args&&... args): Data(std::forward<Args>(args)...) {}
};
private:
class Final {
friend class Event;
private:
Final() {}
Final(const Final &) {}
};
};
The gymanstics with forward-declaration of the Final class are there since forward-declaring the Event class is more typing.
The data carrier is as simple as it gets:
// A string carrier
class StringData {
QString m_str;
public:
explicit StringData(const QString & str) : m_str(str) {}
QString value() const { return m_str; }
};
Usage & Tests (Common Code)
And now we can use the generic metafactory to make some concrete metafactories, and then to make the event classes we need. We create two unique event types that carry the data. Those event classes have unique staticType()s.
// A string event metafactory
template <typename Derived> class StringEventMF : public EventMF<Derived, StringData> {};
class Update : public EventMF<Update, StringData> {}; // using generic metafactory
class Clear : public StringEventMF<Clear> {}; // using specific metafactory
#if 0
// This should fail at compile time as such derivation would produce classes with
// duplicate event types. That's what the Final class was for in the matafactory.
class Error : public Update::Event { Error() : Update::Event("") {} };
#endif
int main(int, char**)
{
// Test that it works as expected.
Update::Event update("update");
Clear::Event clear("clear");
Q_ASSERT(Update::Event::staticType() != Clear::Event::staticType());
Q_ASSERT(Update::Event::staticType() == Update::Event::cast(&update)->staticType());
qDebug() << Update::Event::cast(&update)->value();
Q_ASSERT(Update::Event::cast(&clear) == 0);
qDebug() << Clear::Event::cast(&clear)->value();
Q_ASSERT(Clear::Event::cast(&update) == 0);
}