The following "Event" code snippet shows the "pure virtual function call" error. However, as mentioned in the title, it happens only when deploying on DEBUG. What makes me curious is why it works flawlessly on RELEASE and why it does even crash (on DEBUG).
Alternatively, you can see the snippet here.
#include <list>
#include <iostream>
#include <algorithm>
// use base class to resolve the problem of how to put into collection objects of different types
template <typename TPropertyType>
struct PropertyChangedDelegateBase
{
virtual ~PropertyChangedDelegateBase(){};
virtual void operator()(const TPropertyType& t) = 0;
};
template <typename THandlerOwner, typename TPropertyType>
struct PropertyChangedDelegate : public PropertyChangedDelegateBase<TPropertyType>
{
THandlerOwner* pHandlerOwner_;
typedef void (THandlerOwner::*TPropertyChangeHandler)(const TPropertyType&);
TPropertyChangeHandler handler_;
public:
PropertyChangedDelegate(THandlerOwner* pHandlerOwner, TPropertyChangeHandler handler) :
pHandlerOwner_(pHandlerOwner), handler_(handler){}
void operator()(const TPropertyType& t)
{
(pHandlerOwner_->*handler_)(t);
}
};
template<typename TPropertyType>
class PropertyChangedEvent
{
public:
virtual ~PropertyChangedEvent(){};
void add(PropertyChangedDelegateBase<TPropertyType>* const d)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
if(it != observers_.end())
throw std::runtime_error("Observer already registered");
observers_.push_back(d);
}
void remove(PropertyChangedDelegateBase<TPropertyType>* const d)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
if(it != observers_.end())
observers_.remove(d);
}
// notify
void operator()(const TPropertyType& newValue)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = observers_.begin();
for(; it != observers_.end(); ++it)
{
(*it)->operator()(newValue);
}
}
protected:
std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;
};
class PropertyOwner
{
int property1_;
float property2_;
public:
PropertyChangedEvent<int> property1ChangedEvent;
PropertyChangedEvent<float> property2ChangedEvent;
PropertyOwner() :
property1_(0),
property2_(0.0f)
{}
int property1() const {return property1_;}
void property1(int n)
{
if(property1_ != n)
{
property1_ = n;
property1ChangedEvent(n);
}
}
float property2() const {return property2_;}
void property2(float n)
{
if(property2_ != n)
{
property2_ = n;
property2ChangedEvent(n);
}
}
};
struct PropertyObserver
{
void OnPropertyChanged(const int& newValue)
{
std::cout << "PropertyObserver::OnPropertyChanged() -> new value is: " << newValue << std::endl;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
PropertyOwner propertyOwner;
PropertyObserver propertyObserver;
// register observers
PropertyChangedDelegate<PropertyObserver, int> delegate(&propertyObserver, &PropertyObserver::OnPropertyChanged);
propertyOwner.property1ChangedEvent.add(&delegate); // Ok!
propertyOwner.property1ChangedEvent.add(&PropertyChangedDelegate<PropertyObserver, int>(&propertyObserver, &PropertyObserver::OnPropertyChanged)); // Error: Virtual pure function call (Debug only)
propertyOwner.property1(1);
return getchar();
}
I would assume that the error is misnomer and that the problem is more likely to do with the scope that the second delegate lives. Plus declaring it outside is easier to read.
Passing around an object created on the stack rather than the heap by reference is usually a bad idea. Once the item declaration is out of scope the object is usually forgotten about.
The general issue is that you are binding to a temporary that gets destroyed and thus has an empty vtable and of course it generates a pure virtual call when invoked on the change of the property. If you add a dtor for the base class this is quite easy to observe:
#include <list>
#include <iostream>
#include <algorithm>
// use base class to resolve the problem of how to put into collection objects of different types
template <typename TPropertyType>
struct PropertyChangedDelegateBase
{
virtual ~PropertyChangedDelegateBase(){};
virtual void operator()(const TPropertyType& t) = 0;
};
template <typename THandlerOwner, typename TPropertyType>
struct PropertyChangedDelegate : public PropertyChangedDelegateBase<TPropertyType>
{
THandlerOwner* pHandlerOwner_;
typedef void (THandlerOwner::*TPropertyChangeHandler)(const TPropertyType&);
TPropertyChangeHandler handler_;
public:
PropertyChangedDelegate(THandlerOwner* pHandlerOwner, TPropertyChangeHandler handler) :
pHandlerOwner_(pHandlerOwner), handler_(handler)
{
std::cout << "0x" << std::hex << this << " created!" << std::endl;
}
void operator()(const TPropertyType& t)
{
(pHandlerOwner_->*handler_)(t);
}
~PropertyChangedDelegate()
{
std::cout << "0x" << std::hex << this << " destroyed!" << std::endl;
}
};
template<typename TPropertyType>
class PropertyChangedEvent
{
public:
virtual ~PropertyChangedEvent(){};
void add(PropertyChangedDelegateBase<TPropertyType>* const d)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
if (it != observers_.end())
throw std::runtime_error("Observer already registered");
observers_.push_back(d);
}
void remove(PropertyChangedDelegateBase<TPropertyType>* const d)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = std::find(observers_.begin(), observers_.end(), d);
if (it != observers_.end())
observers_.remove(d);
}
// notify
void operator()(const TPropertyType& newValue)
{
std::list<PropertyChangedDelegateBase<TPropertyType>* const>::const_iterator it = observers_.begin();
for (; it != observers_.end(); ++it)
{
std::cout << "Invoking 0x" << std::hex << *it << std::endl;
(*it)->operator()(newValue);
}
}
protected:
std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;
};
class PropertyOwner
{
int property1_;
float property2_;
public:
PropertyChangedEvent<int> property1ChangedEvent;
PropertyChangedEvent<float> property2ChangedEvent;
PropertyOwner() :
property1_(0),
property2_(0.0f)
{}
int property1() const { return property1_; }
void property1(int n)
{
if (property1_ != n)
{
property1_ = n;
property1ChangedEvent(n);
}
}
float property2() const { return property2_; }
void property2(float n)
{
if (property2_ != n)
{
property2_ = n;
property2ChangedEvent(n);
}
}
};
struct PropertyObserver
{
void OnPropertyChanged(const int& newValue)
{
std::cout << "PropertyObserver::OnPropertyChanged() -> new value is: " << newValue << std::endl;
}
};
int main(int argc, char* argv[])
{
PropertyOwner propertyOwner;
PropertyObserver propertyObserver;
// register observers
PropertyChangedDelegate<PropertyObserver, int> delegate(&propertyObserver, &PropertyObserver::OnPropertyChanged);
propertyOwner.property1ChangedEvent.add(&delegate); // Ok!
propertyOwner.property1ChangedEvent.add(&PropertyChangedDelegate<PropertyObserver, int>(&propertyObserver, &PropertyObserver::OnPropertyChanged)); // Error: Virtual pure function call (Debug only)
propertyOwner.property1(1);
return getchar();
}
Basically you are just running into undefined behavior - the object is destroyed in both cases, but in Release the vtable is not destroyed so you get by.
This:
propertyOwner.property1ChangedEvent.add(
&PropertyChangedDelegate<PropertyObserver, int>(
&propertyObserver,
&PropertyObserver::OnPropertyChanged)
);
You are capturing a pointer to a temporary object PropertyChangedDelegate<PropertyObserver, int>. Pointer to this object becomes invalid as soon as function call is over and temporary is destroyed. Dereferencing this pointer is undefined behavior.
In your program, memory ownership relations are critical and you should think them through carefully.
You need to ensure that all your pointers outlive objects that rely on them, either manually:
PropertyChangedDelegate<PropertyObserver, int> delegate2 = {
&propertyObserver,
&PropertyObserver::OnPropertyChanged
};
propertyOwner.property1ChangedEvent.add(&delegate2);
or by using smart pointers (std::unique_ptr<>, std::shared_ptr<>).
Another bug:
C++11 compliant compier should not allow you doing this:
std::list<PropertyChangedDelegateBase<TPropertyType>* const> observers_;
The error I got with Visual Studio 2015 is:
The C++ Standard forbids containers of const elements because allocator is ill-formed.`
See: Does C++11 allow vector<const T>?
Bonus:
Your C++ style looks quite a bit obsolete.
You might want to try automatic type deduction:
for(auto it = observers_.begin(); it != observers_.end(); ++it)
{
(*it)->operator()(newValue);
}
or, better, ranged for loops:
for(auto observer : observers)
{
observer(newValue);
}
You might want to take a look to:
The Definitive C++ Book Guide and List
C++ Core Guidelines
Related
Assume the snippet below. How can I make this compiling/working? I do not want to move print to the String/Float class, because in my real world situation this function is combining a lot of data.
So basically I want a pointer/member to "any type (in this case string/float)" then use it, and call dynamically something else (in this case print)?
I assume that this does not work (among others) because it cannot determine at compile time which type T of ptr will have at compile time.
What is the general pattern to solve such kind of problems?
#include <iostream>
template<typename T>
class AbstractClass {
virtual T getValue()=0;
};
class StringClass : public AbstractClass<std::string> {
std::string getValue() override {
return "A";
}
};
class FloatClass : public AbstractClass<float> {
float getValue() override {
return 1;
}
};
class Processor {
public:
AbstractClass<T>* ptr;
void doIt() {
ptr=new StringClass();
print(ptr->getValue());
delete ptr;
ptr=new FloatClass();
print(ptr->getValue());
delete ptr;
}
void print(std::string i) {
std::cout << "String "<<i<<std::endl;
}
void print(float i) {
std::cout << "Float "<<i<<std::endl;
}
}
int main() {
Processor a;
a.doIt();
}
If you want an object that's 'one of' a given set of types, you can use std::variant<Ts...>. Mathematically, it represents discriminated/tagged union. This way, you don't need a pointer, neither a base class. Example:
#include <iostream>
#include <variant>
class StringClass {
std::string getValue() override {
return "A";
}
};
class FloatClass {
float getValue() override {
return 1;
}
};
using ClassWithGetValue = std::variant<StringClass, FloatClass>;
class Processor {
public:
ClassWithGetValue v;
void doIt() {
v = StringClass();
std::visit([&](auto&& v1) {
print(v1.getValue());
});
v = FloatClass();
std::visit([&](auto&& v1) {
print(v1.getValue());
});
}
void print(std::string i) {
std::cout << "String "<<i<<std::endl;
}
void print(float i) {
std::cout << "Float "<<i<<std::endl;
}
}
int main() {
Processor a;
a.doIt();
}
I am currently working on a small private project using C++ i came up with the following structure:
#include <iostream>
class A
{
std::vector<int> vec;
protected:
virtual bool onAdd(int toAdd) {
// should the 'adding' be suppressed?
// do some A specific checks
std::cout << "A::onAdd()" << std::endl;
return false;
}
public:
void add(int i) {
if(!onAdd(i)) {
// actual logic
vec.push_back(i);
}
}
};
class B : public A
{
protected:
bool onAdd(int toAdd) override {
// do some B specific checks
std::cout << "B::onAdd()" << std::endl;
return false;
}
};
In this example onAdd is basically meant to be a callback for add, but in a more polymorphic way.
The actual problem arises when a class C inherits from B and wants to override onAdd too. In this case the implementation in B will get discarded (i.e. not called) when calling C::add. So basically what I would like to achieve is a constructor-like behaviour where I am able to override the same method in different positions in the class hierarchy and all of those getting called.
My question now is: Is there a possibility/design to achieve this? I am sure that it wouldn't be as easy as cascading constructors, though.
Note: Don't focus too much on the add example. The question is about the callback like structure and not if it makes sense with an add.
I would just call my parents onAdd()
bool C::onAdd(int toAdd) {return my_answer && B::onAdd(toAdd);}
This can be a little confusing if you're expecting other developers to inherit from your base class. But for small private hierarchies it works perfectly.
I sometimes include a using statement to make this more explicit
class C : public B
{
using parent=B;
bool onAdd(int toAdd) override {return my_answer && parent::onAdd(toAdd);}
};
struct RunAndDiscard {
template<class Sig, class...Args>
void operator()(Sig*const* start, Sig*const* finish, Args&&...args)const{
if (start==finish) return;
for (auto* i = start; i != (finish-1); ++i) {
(*i)(args...);
}
(*(finish-1))(std::forward<Args>(args)...);
}
};
template<class Sig, class Combine=RunAndDiscard>
struct invokers {
std::vector<Sig*> targets;
template<class...Args>
decltype(auto) operator()(Args&&...args)const {
return Combine{}( targets.data(), targets.data()+targets.size(), std::forward<Args>(args)... );
}
};
struct AndTogetherResultWithShortCircuit {
template<class Sig, class...Args>
bool operator()(Sig*const* start, Sig*const* finish, Args&&...args)const{
if (start==finish) return true;
for (auto* i = start; i != (finish-1); ++i) {
if (!(*i)(args...)) return false;
}
return (*(finish-1))(std::forward<Args>(args)...);
}
};
This creates a per-instance table of things to do onAdd.
Creating a per-class table is harder; you need to chain your table with your parent type's table, which requires per-class boilerplate.
There is no way to get the C++ compiler to write either the per-instance version, or the per-class version, without doing it yourself.
There are C++20 proposals involving reflection and reification, plus the metaclass proposal, which may involve automating writing code like this (on both a per-instance and per-class basis).
Here is a live example of this technique being tested:
struct AndTogetherResultWithShortCircuit {
template<class Sig, class...Args>
bool operator()(Sig*const* start, Sig*const* finish, Args&&...args)const{
if (start==finish) return true;
for (auto* i = start; i != (finish-1); ++i) {
if (!(*i)(args...)) return false;
}
return (*(finish-1))(std::forward<Args>(args)...);
}
};
class A {
std::vector<int> vec;
protected:
invokers<bool(A*, int), AndTogetherResultWithShortCircuit> onAdd;
public:
void add(int i) {
if (!onAdd(this, i)) {
vec.push_back(i);
}
}
};
class B : public A
{
public:
B() {
onAdd.targets.push_back([](A* self, int x)->bool{
// do some B specific checks
std::cout << "B::onAdd(" << x << ")" << std::endl;
return x%2;
});
}
};
class C : public B
{
public:
C() {
onAdd.targets.push_back([](A* self, int x)->bool{
// do some B specific checks
std::cout << "C::onAdd(" << x << ")" << std::endl;
return false;
});
}
};
When you want to write your own OO-system, you can in C++, but C++ doesn't write it for you.
If you want a generic solution perhaps you could use CRTP with variadic templates instead of runtime polymophism.
Taking inspiration from this answer and this answer:
template<class... OnAdders> class A : private OnAdders... {
std::vector<int> vec;
template<class OnAdder>
bool onAdd(int toAdd){
return static_cast<OnAdder*>(this)->onAdd(toAdd);
}
template<typename FirstOnAdder, typename SecondOnAdder, class... RestOnAdders>
bool onAdd(int toAdd){
if (onAdd<FirstOnAdder>(toAdd))
return true;
return onAdd<SecondOnAdder, RestOnAdders...>(toAdd);
}
public:
void add(int i) {
if (onAdd<OnAdders...>(i))
return;
// actual logic
vec.push_back(i);
}
};
class B {
public:
bool onAdd(int toAdd) {
// do some B specific checks
std::cout << "B::onAdd()" << std::endl;
return false;
}
};
Which you could use like:
A<B,C> a;
a.add(42);
Live demo.
The following solution uses std::function to add each callback during each constructor:
#include <iostream>
#include <vector>
#include <functional>
class A
{
std::vector<int> vec;
protected:
bool onAdd(int toAdd)
{
// do some A specific checks
std::cout << "A::onAdd()" << std::endl;
return true;
}
// vector of callback functions. Initialized with A::onAdd() callback as the first entry
std::vector<std::function<bool(int)>> callbacks{{[this](int toAdd){return onAdd(toAdd); }}};
public:
void add(int i)
{
for(auto& callback : callbacks) {
if(!callback(i))
return;
}
// actual logic
vec.push_back(i);
}
};
class B : public A
{
public:
B()
{
callbacks.emplace_back([this](int toAdd){return onAdd(toAdd); });
}
protected:
bool onAdd(int toAdd)
{
// do some B specific checks
std::cout << "B::onAdd()" << std::endl;
return true;
}
};
class C : public B
{
public:
C()
{
callbacks.emplace_back([this](int toAdd){return onAdd(toAdd); });
}
protected:
bool onAdd(int toAdd)
{
// do some C specific checks
std::cout << "C::onAdd()" << std::endl;
// must also call B::onAdd()
return true;
}
};
int main()
{
C c;
c.add(5);
}
Prints:
A::onAdd()
B::onAdd()
C::onAdd()
#include <iostream>
struct object1 {
object1(int v) : type(1), value(v) {}
int type;
int value;
};
struct object2 {
object2(int v) : type(2), value(v) {}
int type;
int value;
};
template <typename HeaderType>
void foo(HeaderType * hdr) {
std::cout << "foo called with type " << hdr->type << " and value " << hdr->value << std::endl;
}
// this function doesn't work
template <typename HandlerType>
void dispatch(int type, int val, HandlerType handler) {
if (type == 1) {
object1 h(val);
handler(&h);
} else {
object2 h(val);
handler(&h);
}
}
int main() {
int type = 1;
int val = 1;
// this part works
if (type == 1) {
object1 h(val);
foo(&h);
} else {
object2 h(val);
foo(&h);
}
// trying to replicate the above behavior in a more abstract way,
// ideally via a function call of the following sort
//
// dispatch(type, val, ..foo..? );
}
The above program takes an input value, uses it to decide what type of object to create, then calls a function foo with a pointer to that object.
Question: Is it possible to create this sort of abstraction where the caller of dispatch doesn't know the exact types that foo will be called with but the dispatch function doesn't know the specific function that is going to be called?
With
template <typename HandlerType>
void dispatch(int type, int val, HandlerType handler) {
if (type == 1) {
object1 h1(val);
handler(&h1);
} else {
object2 h2(val);
handler(&h2);
}
}
All branches should be valid, so handler(&h1) and handler(&h2) should be valid calls.
For that, handler may be a generic lambda (since C++14) as suggested in comment:
dispatch(type, val, [](auto a) {return foo(a);} );
or you may create your own functor:
struct foo_caller
{
template <typename HeaderType>
void operator () (const HeaderType* hdr) const {
std::cout << "foo called with type " << hdr->type << " and value " << hdr->value << std::endl;
}
};
And then call it:
dispatch(type, val, foo_caller());
I'm trying to fix a double free or corruption in this class:
struct Holder
{
template <typename T>
Holder(const T& v)
{
_v = new T{};
memcpy(_v, &v, sizeof(T));
_deleter = [this]{
if (_v != nullptr)
{
delete reinterpret_cast<T*>(_v);
_v = nullptr;
}
};
}
template <typename T>
T get()
{
T t;
memcpy(&t, _v, sizeof(T));
return t;
}
~Holder()
{
std::cout << "~Holder() " << std::endl;
_deleter();
}
private:
void* _v;
std::function<void()> _deleter;
};
The goal of this class is to Hold a value of a particular type, like boost::any. So I'm trying to understand the mechanism to safely deallocate all memory.
Probably this line of code:
delete reinterpret_cast<T*>(_v);
doesn't do what I expect ...
**** After Suggestions ****
I've rewrite the code using comment suggestions and adding a move constructor
struct Holder
{
template <typename T>
Holder(const T& v)
{
std::cerr << "create " << N << std::endl;
_v = new T(v);
_deleter = [this]{
if (_v != nullptr)
{
std::cerr << "deleter " << N << std::endl;
delete reinterpret_cast<T*>(_v);
_v = nullptr;
}
};
}
Holder(Holder&& rs)
{
_v = rs._v;
_deleter = std::move(rs._deleter);
rs._deleter = []{}; //usefull to avoid a bad function call
}
template <typename T>
T get() const
{
return *reinterpret_cast<T*>(_v);
}
~Holder()
{
//std::cout << "~Holder() " << N << std::endl;
_deleter();
}
private:
void* _v;
std::function<void()> _deleter;
};
Now seems work but I have to manage others corner case :)
Probably the best solution is to use boost::any:
struct Holder
{
template <typename T>
Holder(const T& v)
{
_v = v;
}
template <typename T>
T get()
{
return boost::any_cast<T>(_v);
}
private:
boost::any _v;
};
But I'am trying to understand how it coudl works without it.
This is my last version:
struct Holder
{
template <typename T>
Holder(const T& v)
{
std::cerr << "create " << N << std::endl;
_v = new T(v);
_deleter = [](void* ptr){
if (ptr != nullptr)
{
std::cerr << "deleter " << std::endl;
delete reinterpret_cast<T*>(ptr);
}
};
_builder = [](void* &dest, void* src){
dest = new T(*reinterpret_cast<T*>(src));
};
}
Holder(const Holder& rs)
{
std::cerr << "copy constr" << std::endl;
if (this != &rs)
{
rs._builder(_v, rs._v);
_deleter = rs._deleter;
_builder = rs._builder;
}
}
Holder(Holder&& rs)
{
std::cerr << "move constr" << std::endl;
if (this != &rs)
{
_v = rs._v;
_deleter = std::move(rs._deleter);
_builder = std::move(rs._builder);
rs._deleter = [](void*){};
}
}
Holder& operator=(const Holder& rs)
{
std::cerr << "copy operator" << std::endl;
if (this != &rs)
{
rs._builder(_v, rs._v);
_deleter = rs._deleter;
_builder = rs._builder;
}
return *this;
}
Holder& operator=(Holder&& rs)
{
std::cerr << "move operator" << std::endl;
if (this != &rs)
{
_v = rs._v;
_deleter = std::move(rs._deleter);
_builder = std::move(rs._builder);
rs._deleter = [](void*){};
}
return *this;
}
template <typename T>
T get() const
{
return *reinterpret_cast<T*>(_v);
}
~Holder()
{
//std::cout << "~Holder() " << N << std::endl;
_deleter(_v);
}
private:
void* _v;
std::function<void(void* ptr)> _deleter;
std::function<void(void* &, void* src)> _builder;
};
Don't reimplement the horse.
using pvoid_holder = std::unique_ptr<void, std::function<void(void*)>>
template<class T>
pvoid_holder pvoid_it( T* t ) {
return { t, [](void* v){ if (v) delete static_cast<T*>(v); } };
}
Now store a pvoid_holder in your Holder class. It will handle memory lifetime for you.
You could use a naked pvoid_holder, but it might have a richer interface than you want (for example, it will allow the stored pointer to be changed without changing the deleter).
You can also replace std::function with void(*)(void*) for a marginal performance gain.
Here is a random idea. I still don't like it though. The whole idea behind this design is bad.
template <typename T>
struct Holder
{
public:
Holder(T const& v)
{
new (&m_v) T(v);
}
T const& get() const
{
return reinterpret_cast<T const&>(m_v);
}
T& get()
{
return reinterpret_cast<T&>(m_v);
}
~Holder()
{
std::cout << "~Holder() " << std::endl;
get().~T();
}
private:
char m_v[sizeof(T)];
};
This class doesn't do the same as yours anymore, ie it can't store arbitrary types in a std::vector<Holder> but only the same type (std::vector<Holder<Foo>>). A comment was too small to contain this code though and I wanted to show a better looking syntax for what you're playing with ;).
That being said, the only way you can do what you're trying to do is when you add a second layer for the reference counting. Ie, you replace your void* _v with something that resembles shared_ptr but which doesn't call delete when the count reaches zero but calls deleter (which therefore should be stored inside this new class). In fact your class looks mostly like this new class, except that you should make it non-copyable and provide reference counting (ie through boost::intrusive_ptr). Then Holder can be a wrapper around that that is copyable.
Probably this line of code: delete reinterpret_cast<T*>(_v); doesn't do what I expect ...
Not exactly. Your types are likely using a default copy ctor; this copies your data pointer _v, and your deleter. So when both objects destruct, both deleters trigger, causing the data to be deleted twice. (Side note--you shouldn't name variables starting with _; such identifiers are reserved for implementations).
Here's what it takes to do type erasure properly, assuming I've no bugs in it. A better way would be to stick to using boost::any.
#include <utility>
struct EmptyType {}; // Thrown if unexpectedly empty
struct InvalidType {}; // Thrown if Holder(T) but get<U>.
struct Holder
{
Holder()
: data_()
, deleter_(e_deleter)
, copier_(e_copier)
, typetag_()
{
}
template<typename T>
Holder(const T& t)
: data_(erase_cast(new T))
, deleter_(deleter<T>)
// Need to explicitly carry T's copy behavior
// because Holder's default copy ctor isn't going to
, copier_(copier<T>)
// You need some way to protect against getting
// an Orange out of a Holder that holds an Apple.
, typetag_(id<T>())
{
}
Holder(const Holder& rhs)
: data_(rhs.copy())
, deleter_(rhs.deleter_)
, copier_(rhs.copier_)
, typetag_(rhs.typetag_)
{
}
template<typename T>
T get()
{
if (!data_) throw EmptyType();
T rv(fetch<T>());
return rv;
}
Holder(Holder&& rhs)
: data_()
, copier_(rhs.copier_)
, deleter_(rhs.deleter_)
, typetag_(rhs.typetag_)
{
std::swap(data_, rhs.data_);
}
~Holder()
{
destroy();
}
private:
// Reinterpret_cast wrappers labeled semantically
template<typename T>
static void* erase_cast(T* t) { return reinterpret_cast<void*>(t); }
template<typename T>
static T* unerase_cast(void* t) { return reinterpret_cast<T*>(t); }
// Return a data copy
void* copy() const { return copier_(data_); }
// Return const reference to data
template<typename T>
const T& fetch() {
if (typetag_!=id<T>()) throw InvalidType();
return *unerase_cast<T>(data_);
}
// Destroy data
void destroy() { deleter_(data_); data_=0; }
// ==== Type erased copy semantics ===
void*(*copier_)(void*);
template<typename T>
static void* copier(void* v) {
return erase_cast<T>(new T(*unerase_cast<T>(v)));
}
static void* e_copier(void*) { return 0; }
// ==== Type erased delete semantics ===
void(*deleter_)(void*);
template<typename T>
static void deleter(void* v) {
delete unerase_cast<T>(v);
}
static void e_deleter(void*) {}
// ==== Type protection using tagging (could also use typeid)
static int makenewid() { static int i=0; return i++;}
template<typename T>
static int id() { static int i=makenewid(); return i; }
// Type erased data
void* data_;
// Type erased tag
int typetag_;
};
...and here is some test/demo code:
#include <iostream>
#include <vector>
#define FAIL() std::cout << "Fail" << std::endl; return 1
int foos=0;
struct Foo { Foo(){++foos;} Foo(const Foo&){++foos;} ~Foo(){--foos;} };
int bars=0;
struct Bar { Bar(){++bars;} Bar(const Bar&){++bars;} ~Bar(){--bars;} };
int main() {
{
std::vector<Holder> v;
Foo fx,fy,fz; Bar ba,bb;
v.push_back(fx); v.push_back(fy); v.push_back(fz);
v.push_back(ba); v.push_back(ba); v.push_back(bb);
v.push_back(Holder());
try {
Foo y = v[2].get<Foo>();
}
catch (EmptyType&) { FAIL(); }
catch (InvalidType&) { FAIL(); }
try {
Foo y = v[4].get<Foo>();
FAIL();
}
catch (EmptyType&) { FAIL(); }
catch (InvalidType&) { }
try {
Foo y = v[6].get<Foo>();
FAIL();
}
catch (EmptyType&) { }
catch (InvalidType&) { FAIL(); }
}
if (foos||bars) { FAIL(); }
std::cout << "Pass" << std::endl;
}
Test results:
$ ./a.exe
Pass
I have the following code with a custom Variant class and a custom SmartPtr class:
using namespace std;
class Object
{
public:
};
template<typename T>
class SmartPtr
{
public:
template<typename Y>
explicit SmartPtr(Y* p) { p_ = p; }
SmartPtr(std::nullptr_t) { p_ = nullptr; }
private:
T* p_;
};
class Variant
{
public:
Variant(bool b) : _b(b) { }
private:
bool _b;
};
class Obj
{
public:
void test(SmartPtr<Object> /*p*/) { cout << "smartptr version!" << endl; }
void test(Variant /*v*/) { cout << "variant version!" << endl; }
};
int main(int argc, const char *argv[])
{
Obj o;
o.test(nullptr); // calls SmartPtr version
o.test(true); // calls Variant version
o.test(false); // -> compiler error: ambiguous call to overloaded function
return 0;
}
I assume that the boolean false can be converted both to the Variant and to 0 then to nullptr and then to SmartPtr, which causes this error.
Any chances of avoiding this conversion?
For the user of the library an API which works with 'o.test(true);' but requires something like 'o.test(Variant(false));' to compile is not very intuitive.
I believe I have an ideal solution. It only requires that the test function be altered, so it leaves SmartPtr and Variant alone, which is ideal. It adds a non-defined templated overload to test that has specializations for bool and nullptr that are defined. This directly dispatches bool and nullptr to the desired specialization, but causes link errors on other unhandled types. I'm so glad to have this worked out because I've certainly run into this in many forms myself. I wish you could use explicit of function parameters!!
I got the idea from here: C++ templates that accept only certain types
using namespace std;
class Object
{
public:
};
class Variant
{
public:
Variant( bool b) : _b(b) { }
private:
bool _b;
};
template<typename T>
class SmartPtr
{
public:
SmartPtr(std::nullptr_t null) { p_ = nullptr; }
template<typename Y>
SmartPtr(Y* p) { p_ = p; }
private:
T* p_;
};
class Obj
{
public:
void test(SmartPtr<Object> here /*p*/) {
cout << "smartptr version!" << endl;
}
void test(Variant /*v*/) { cout << "variant version!" << endl; }
template<typename T> void test(T t);
template<>
void test<bool>(bool b) {
cout << "bool specialization" << endl;
test(Variant(b));
}
template<>
void test<std::nullptr_t>(std::nullptr_t null) {
cout << "nullptr specialization" << endl;
test(SmartPtr<Object>(nullptr));
}
};
int main(int argc, const char *argv[])
{
Obj o;
Obj c;
Object object;
//o.test(3); // Gives link error LNK2019
o.test(Variant(true)); // calls Variant version
o.test(SmartPtr<Object>(&object)); // calls SmartPtr version
o.test(nullptr); // dispatched to SmartPtr version by nullptr specialization
o.test(true); // dispatched to Variant version by bool specialization
o.test(false); // dispatched to Variant version by bool specialization
return 0;
}
I had already answered with something not ideal, so I leave that answer in tact as what follows:
=============================================
I don't have an ideal solution here, and I don't know the constraints you have on your code so this may not be of functional use to you, but the following is sensible. It disallows code to use nullptr at compile time and relies on a global null_smart constant to be used in all cases where the caller is simply showing no interest in passing an object.
#include <iostream>
using namespace std;
class Object
{
public:
};
class Variant
{
public:
Variant(bool b) : _b(b) { }
private:
Variant(std::nullptr_t) {};
private:
bool _b;
};
template<typename T>
class SmartPtr
{
public:
SmartPtr() { p_ = nullptr; }
template<typename Y>
SmartPtr(Y* p) { p_ = p; }
private:
T* p_;
};
class Obj
{
public:
void test(SmartPtr<Object> /*p*/) { cout << "smartptr version!" << endl; }
void test(Variant /*v*/) { cout << "variant version!" << endl; }
};
const SmartPtr<Object> null_smart;
int main(int argc, const char *argv[])
{
Obj o;
o.test(null_smart); // calls SmartPtr version, without interest in passing object
o.test(true); // calls Variant version
o.test(false); // calls Variant version
return 0;
}
It's cleaner than the true/Variant(false) issue, but still a bit on the picky side.