Can a std::map contain a reference to a constructor? - c++

Is there a way to point to a constructor from a std::map? I'd like to do the following, with the code I desire to use in #if 0, but I can't seem to get this to work:
#include <map>
#include <functional>
using namespace std;
class Base { };
class A : public Base { };
class B : public Base { };
enum class Type { A, B, };
#if 0
using type_map_t = std::map<Type, std::function<Base*()>>;
type_map_t type_map = {
{Type::A, &A::A},
{Type::B, &B::B},
};
#endif
Base*
getBase(Type t)
{
#if 0
auto constructor = type_map[t];
return constructor();
#else
switch(t)
{
case Type::A:
return new A();
case Type::B:
return new B();
}
#endif
}
int
main(int argc, char *argv[])
{
Base *base = getBase(Type::A);
return 0;
}
Rather than have a switch statement in getBase, I'd rather have the map indicate what constructor gets called for each type.
std::function comes to mind for how to do this, but it doesn't seem possible to get the address of a constructor in C++. Is there an elegant way to accomplish what I want to do here?

You cannot take the address of a constructor according to C++ standard
(§ 12.1.12 of C++98/03 and § 12.1.10 of C++11):
Constructors - The address of a constructor shall not be taken.
For this problem the typical solution is to create specific factories/methods that creates object.

A couple of points:
A constructor is a bit of code that takes raw memory and turns it into an object. This means you have to have exactly the right amount of memory available.
There is no mechanism for you to access a constructor like a normal function because of that: the constructor's this pointer is already known when construction starts, and there's no way to pass one in.
The way around these limitations is to use operator new. The usual operator new will allocate the memory needed for the object and apply the constructor code if allocation was successful. (Alternatively, there is a "placement new" that allows the programmer to provide a pointer to "enough memory" for the object. It's used for "emplaced" construction, where you've allocated a suitable buffer beforehand. Generally that stuff lives in container libraries only.)
So what you put in your map is going to have to use new in the functions. The usual new will do since you don't have any mechanism for passing in raw memory.
Using lambdas, your map could look like this:
type_map_t type_map = {
{Type::A, []() -> Base* { return new A; } },
{Type::B, []() -> Base* { return new B; } },
};
The construct []() -> Base* says that the following code block is treated as a function body taking no arguments (nothing in the ()-list), and nothing from surrounding scope (nothing in the []-list), and returning a Base*. It's usable as an initialization value for the std::function<Base*()> holder object.
You can call your map entry directly too:
Base* base = type_map[Type::A]();

While directly getting a pointer to a ctor is not possible, you can create your own ctor objects:
template<class Sig>
struct ctor_t;
template<class T, class...Args>
struct ctor_t<T(Args...)> {
T* operator()(Args...args)const {
return new T(std::forward<Args>(args)...);
}
T* operator()(void*p, Args...args)const {
return new(p) T(std::forward<Args>(args)...);
}
using simple = T*(*)(Args...args);
using placement = T*(*)(void*, Args...args);
operator simple()const {
return [](Args...args)->T* {
return ctor_t{}(std::forward<Args>(args)...);
};
}
operator placement()const {
return [](void*p, Args...args)->T* {
return ctor_t{}(p, std::forward<Args>(args)...);
};
}
};
template<class Sig>
static const ctor_t<Sig> ctor = {};
which lets you create an object that acts like a ctor and can be turned into a function pointer as well.
live example.
The above uses some C++14. Replace ctor with:
template<class Sig>
constexpr ctor_t<Sig> ctor() { return {}; }
and its use from ctor<A()> to ctor<A()>() for C++11 (without variable templates).

Related

boost::allocate_unique yields non-deafult constructible and non-move assignable unique_ptrs

I have a question about allocate_unique from Boost. It looks like that resulting unique_ptrs are quite limited - they cannot be default constructed to nullptr without providing a deleter (even an invalid one), and also, move assignment does not work.
Luckily, move construction does work, so I was able to hack around not having move assignment by calling the destructor and move-constructing using placement new.
Is it a defect in Boost that alloc_deleter is not moveable, and thus disables move assignment of these unique_ptrs? Or am I misunderstanding something?
#include <memory>
#include <memory_resource>
#include <boost/smart_ptr/allocate_unique.hpp>
#include <iostream>
using Pma = std::pmr::polymorphic_allocator<std::byte>;
template<typename T> using pmr_deleter = boost::alloc_deleter<T, Pma>;
template<typename T> using pmr_unique_ptr = std::unique_ptr<T, pmr_deleter<T>>;
struct Vertex {
float x = 1;
float y = 2;
float z = 3;
};
int main() {
auto& res = *std::pmr::new_delete_resource();
pmr_deleter<Vertex> d(nullptr);
pmr_unique_ptr<Vertex> v_empty(nullptr, d); // will not default construct without deleter??
pmr_unique_ptr<Vertex> v = boost::allocate_unique<Vertex>(Pma(&res), Vertex{7,8,9});
// v_empty = std::move(v); // operator=(unique_ptr&&) gets deleted because `alloc_deleter` is not moveable!
// We can hack in a move like this:
v_empty.~pmr_unique_ptr<Vertex>();
new (&v_empty) pmr_unique_ptr<Vertex>(v.get(), v.get_deleter());
v.release();
std::cout << v_empty->x << "," << v_empty->y << "," << v_empty->z << std::endl;
return 0;
}
Polymorphic allocators are stateful, which means they cannot be default-constructed - because they wouldn't know about the memory resource they're supposed to work with.
This is not particular to PMR or unique pointers, it will also crop up when e.g. using Boost Interprocess allocators on a vector - you will always have to pass an initializer for the allocator.
Ifff you want a global/singleton memory resource, you can obviously declare a custom deleter that encodes that constant:
template <typename T> struct pmr_deleter : boost::alloc_deleter<T, Pma> {
pmr_deleter()
: boost::alloc_deleter<T, Pma>(std::pmr::new_delete_resource()) {}
};
This would allow the default constructor(s) to work:
pmr_unique_ptr<Vertex> v_empty; // FINE
pmr_unique_ptr<Vertex> v_empty(nullptr); // ALSO FINE
However it comes at the cost of no longer being type-compatible with the allocate_unique factory return type (alloc_deleter).
You can probably pave hack this, but I think it would probably be best to understand the situation before you decide whether that's worth it. (Hint: I don't think it is, because it is precisely the goal of PMR to type erase the allocator difference, trading in runtime state instead. If you go and move all state into the allocator again, you effectively made it a static allocator again, which is where we would have been without PMR anyways)
Other Notes
pmr_deleter<Vertex> d(nullptr);
Is ill-formed, as the argument may never be null. Both the compiler will warn about this at -Wnon-null, just as Asan/UBSan will:
/home/sehe/Projects/stackoverflow/test.cpp:18:34: runtime error: null pointer passed as argument 2, which is declared to never be null
Here is a wrapper I wrote around the specialization of std::unique_ptr<T, boost::alloc_deleter>. The unique pointer returned by boost::allocate_unique is implicitly convertible to the wrapper. The wrapper is default constructible, move-assignable and also has .get() return a raw pointer instead of a boost fancy pointer type (which requires an additional .ptr() to get a raw pointer).
The only downside is that you have to use the wrapper explicitly instead of e.g. auto with boost::allocate_unique.
using Pma = std::pmr::polymorphic_allocator<std::byte>;
template<typename T> using pmr_deleter = boost::alloc_deleter<T, Pma>;
template<typename T> class pmr_unique_ptr : public std::unique_ptr<T, pmr_deleter<T>> {
public:
using std::unique_ptr<T, pmr_deleter<T>>::unique_ptr;
T* get() const { return std::unique_ptr<T, pmr_deleter<T>>::get().ptr(); }
pmr_unique_ptr() : std::unique_ptr<T, pmr_deleter<T>>(nullptr, pmr_deleter<T>(std::pmr::null_memory_resource())) { }
pmr_unique_ptr(decltype(nullptr)) : pmr_unique_ptr() { }
template<typename P>
pmr_unique_ptr(std::unique_ptr<P, pmr_deleter<P>>&& p)
: pmr_unique_ptr(static_cast<T*>(p.get().ptr()), *reinterpret_cast<pmr_deleter<T>*>(&p.get_deleter())) {
p.release();
}
template<>
pmr_unique_ptr(std::unique_ptr<T, pmr_deleter<T>>&& p) : std::unique_ptr<T, pmr_deleter<T>>(std::move(p)) { };
pmr_unique_ptr(T* p, pmr_deleter<T> d) : std::unique_ptr<T, pmr_deleter<T>>(boost::detail::sp_alloc_ptr<T,T *>(1, p), d) { };
pmr_unique_ptr(const pmr_unique_ptr&) = delete;
pmr_unique_ptr(pmr_unique_ptr&& p) : std::unique_ptr<T, pmr_deleter<T>>(std::move(p)) { }
template<typename P> operator pmr_unique_ptr<P>() {
P* basep = static_cast<P*>(get());
pmr_deleter<P> d(*reinterpret_cast<pmr_deleter<P>*>(&this->get_deleter()));
this->release();
return {basep, std::move(d)};
}
pmr_unique_ptr& operator=(pmr_unique_ptr&& other) {
this->std::unique_ptr<T, pmr_deleter<T>>::~unique_ptr();
new (static_cast<std::unique_ptr<T, pmr_deleter<T>>*>(this)) std::unique_ptr<T, pmr_deleter<T>>(std::move(other));
return *this;
}
template<typename P> pmr_unique_ptr& operator=(std::unique_ptr<P, pmr_deleter<P>>&& p) {
return operator=(pmr_unique_ptr(pmr_unique_ptr<P>(std::move(p))));
}
};
Example which compiles:
#include <memory_resource>
#include <boost/smart_ptr/allocate_unique.hpp>
// ... the definitions from above
// ...
pmr_unique_ptr<int> p;
pmr_unique_ptr<int> p2 = nullptr;
p2 = boost::allocate_unique<int>(Pma(std::pmr::new_delete_resource()), 5);
p = std::move(p2);
int *rawp = p.get();

What is the preferred way to store one or no object in c++?

In the spirit of "choose your containers wisely", I am interested in what is the best way to store either exactly one or no object, for example as a member in a class. This could be the case, e.g., if the object being held is expensive to calculate and should be cached in some way (or any other type of "late" creation).
The obvious candidates are std::vector and std::unique_ptr, for example:
class object_t;
class foo_t {
std::unique_ptr<object_t> m_cache;
public:
object_t getObject() {
if( not m_cache ) {
m_cache.reset(new object_t()); // object creation is expensive
}
return object_t(*m_cache);
}
};
and similarly with vector (or almost any other container):
class object_t;
class foo_t {
std::vector<object_t> m_cache;
public:
object_t getObject() {
if( m_cache.empty() ) {
m_cache.push_back(object_t()); // object creation is expensive
}
return m_cache.front();
}
};
Of course, there is still the possibility to have some boolean variable, which holds the state of the object:
class object_t;
class foo_t {
bool cache_healthy;
object_t m_cache;
public:
foo_t() : cache_healthy(false), m_cache() {}
object_t getObject() {
if( not cache_healthy ) {
m_cache = object_t();
cache_healthy = true;
}
return m_cache;
}
/* do other things that might set cache_healthy to false. */
};
From the three examples, I like the last one the less, because it either creates the object twice, or, if I change object_t to have a "cheap" / incomplete constructor, might return a invalid object.
The solution with the vector I dislike more semantically, because a vector (or any other container type) might give the impression that there might be more than just one object.
Now thinking of it again, I think I like the pointer solution most, however, still am not entirely happy with it and would like to hear if you know of any solution that is the most elegant in this case.
The "obvious" solution is using boost::optional or (in C++17) std::optional.
An implementation of something like this could look like the following:
template <typename T>
class optional
{
public:
optional() : m_isset(false) {}
template <typename ...Args>
optional(Args... args) {
m_isset = true;
new (&m_data[0]) optional { args... };
}
// overload operator-> and operator* by reinterpret_casting m_data, throwing exceptions if isset == false
private:
bool m_isset;
char m_data[sizeof(T)];
}
The disadvantages of your solutions are unneeded heap allocation in 1 and 2 and reliance on a copy in 3.

C++, adding temporary objects to a list, without dynamic memory allocation

I'm writing code for an embedded platform, therefore I cannot use the normal new operator.
Now I want to add arbitrary objects to a list, just like this.
tp.add(DerivedA("David"));
tp.add(DerivedB("Max"));
tp.add(DerivedC("Thomas"));
For the reason of code duplication I don't want to write something like this:
DerivedA david("David");
tp.add(david);
...
A solution, but not very pretty style would be this:
tp.add(new (myalloc(sizeof(DerivedB))) DerivedB("John"));
// using placement-new works
Now I tried to add a temporary object, passed by pointer:
tp.add(&DerivedA("David"));
Theoretically this could work, but the compiler complains (with good reason) about passing a pointer to a temporary object (-fpermissive).
Is there a clean way of doing what I want to?
Here is a full example:
#include <iostream>
using namespace std;
class Base // base class
{
public:
Base();
int size;
char name[100];
};
class Derived:public Base
{
public:
Derived(char* name);
};
class ThirdParty
{
public:
void add(Base* obj);
void addTemp(Base* tempObj);
Base* list[10];
int index;
};
void* myalloc(int size){
void* p;
// ...
// allocate memory in a static memory pool
// ...
return p;
}
void memcpy(void* to, void* from, int size){
}
int main()
{
ThirdParty tp;
// The ugly style:
tp.add(new (myalloc(sizeof(Derived))) Derived("John")); // using placement-new works
// The beauty style (compiler complains here):
tp.addTemp(&Derived("David")); // create temporary object here, which is copied and added to the list
tp.addTemp(&Derived("Max"));
tp.addTemp(&Derived("Thomas"));
return 0;
}
Base::Base()
{
size = sizeof(Base);
}
Derived::Derived(char *name)
{
size = sizeof(Derived); // make size of this object available for a base-pointer
}
void ThirdParty::add(Base *obj)
{
list[index++] = obj;
}
void ThirdParty::addTemp(Base* tempObj)
{
Base* newObj = (Base*) myalloc(tempObj->size); // let third party allocate memory
memcpy(newObj, tempObj, tempObj->size); // copy the temporary object
list[index++] = newObj;
}
If you use C++11, you could write a forwarding function to do the work for you:
template <typename T, typename... Args>
T* make (Args&&... args) {
return new (myalloc(sizeof(T))) T { std::forward<Args>(args)... };
}
You'd then add an object to your list like so:
tp.add(make<Derived>("John"));
My preferred solution now is the following macro:
#define m(x) new (myalloc(sizeof(x))) x
now I can add a new object with this code:
tp.add(m(Derived("Isabella")));
Can you not just override new to use myalloc ? If you do notcwant to do this globally, you certainly can do it for Base

auto x = make_x(...) and this

I have a class template S<T> and because the template parameter is sometimes hard write explicitly I also have a little helper function makeS(...) to deduce the template parameter.
Now the problem is that the constructor of S has a "side effect": it adds itself to a map which then will later be used to iterate over all instances of S. But this effectively makes S<T> s{...}; very different from auto s = makeS(...); (if RVO is not used).
Here is some code which hopefully shows what I'm trying to do. (Note: in the actual program, S has more than a single template parameter and all will be deduced in makeS)
#include <cassert>
#include <iostream>
#include <map>
#include <string>
#include <utility>
using namespace std;
struct Base
{
virtual ~Base() {}
virtual void f() const = 0;
};
static map<string, Base*> Map;
template <typename T>
struct S : Base
{
T Func;
Base* This;
S(string const& name, T func) : Func(std::move(func)), This(this)
{
//
// Automatically add this struct to Map...
//
Map.insert({name, this});
}
virtual void f() const override { Func(); }
};
template <typename T>
S<T> makeS(std::string const& name, T func)
{
return S<T>(name, std::move(func));
}
void func1()
{
std::cout << "func1\n";
}
int main()
{
struct Func2
{
void operator ()() const {
std::cout << "func2\n";
}
};
//
// This is not possible:
//
// S< ??? > s("s", [](){});
//
// This would be ok:
//
// auto F = [](){};
// S<decltype(F)> s("s", F);
//
auto s1 = makeS("s1", func1);
auto s2 = makeS("s2", Func2());
auto s3 = makeS("s3", [](){ std::cout << "func3\n"; });
//
// Manually adding s1,... to the Map is ok, but that's what
// I want to avoid...
//
// Map.insert({"s1", &s1});
// ...
//
assert(&s1 == s1.This);
assert(&s2 == s2.This);
assert(&s3 == s3.This);
for (auto&& I : Map)
{
I.second->f();
}
}
As I understand it, the map will only contain valid pointers if RVO is used in auto s1 = makeS(...) etc. and this is not guaranteed.
Is there a way to deduce the template parameters while at the same time avoiding the need to manually register s1,...?
Your basic problem is you failed to implement the rule of 3. If your destructor needs non-trivial behavior (and if you register yourself in the constructor, this is the case), you must either implement or block assignment and copy construct (and/or move-assign and move-construct).
In this case, we can implement a move-construct and block move-assign, and copy construct and copy assign are implicitly blocked.
First, add name to S. Then implement a move constructor.
template <typename T>
struct S : Base
{
std::string Name;
T Func;
Base* This; // ?? why ?? this looks both dangerous and useless at the same time!
S( S&& s ): Name(std::move(s.Name)), Func(std::move(s.Func)), This(this) {
s.clear(); // technically `move` need not clear.
map[Name] = this; // overwrite
}
S& operator=(S&& s) = delete; // or implement it
now your object is moveable, and when moved it updates the Map. ~S should, I assume, unregister from Map -- detect if your name is empty (and assert at construction you gain a non-empty name), and if it is don't unregister (as you where already moved from).
Now move-construct and elided-construct have the same semantics. RVO failure results in some inefficiency, but no logical failure. Plus, your type is now moveable, which tends to be really useful.
If you need to maintain object identity, use can use std::unique_ptr:
template <typename T>
std::unique_ptr<S<T>> makeS(std::string const& name, T func)
{
return { new S<T>(name, std::move(func)) };
}
Now moving the pointer from place to place won't move the object; the pointer kept in the map will stay valid.
My suggestions to improve your code:
1) Get rid of the side effect in the constructor. Create objects in a factory method only (makeS in your code; you can make it a static member function of S) and register the S objects in that method. To disable object creation in different ways make constructor(s) private.
2) Disable S objects copying/moving and handle the objects for example as shared_ptr/unique_ptr<S> only. When you disable copying/moving you avoid the problem when your map contains a pointer to invalid objects and now you don't need to rely on RVO.
3) Use std::function<void()> instead of T Func;. In that case your class don't need to be a template class (or it will have less template arguments). That will simplify your code.

Initializing a std::unique_ptr by passing the address of the pointer

I am creating a class which interops with some Windows API code, now one of the pointers I have to initialize is done by calling a native function which initializes it.
My pointers are of type std::unique_ptr with a custom deleter, which calls the WinAPI deleter function provided, however I cannot pass the unique_ptr with the & address-of operator to the init-function. Why?
I have created a sample that demonstrates my problem:
#include <memory>
struct foo
{
int x;
};
struct custom_deleter {};
void init_foo(foo** init)
{
*init = new foo();
}
int main()
{
std::unique_ptr<foo, custom_deleter> foo_ptr;
init_foo(&foo_ptr);
}
The compiler barks and says:
source.cpp: In function 'int main()':
source.cpp:19:21: error: cannot convert 'std::unique_ptr<foo, custom_deleter>*' to 'foo**' for argument '1' to 'void init_foo(foo**)'
Somewhere under the covers, unique_ptr<foo> has a data member of type foo*.
However, it's not legitimate for a user of the class to directly modify that data member. Doing so would not necessarily preserve the class invariants of unique_ptr, in particular it wouldn't free the old pointer value (if any). In your special case you don't need that to happen, because the previous value is 0, but in general it should happen.
For that reason unique_ptr doesn't provide access to the data member, only to a copy of its value (via get() and operator->). You can't get a foo** out of your unique_ptr.
You could instead write:
foo *tmp;
init_foo(&tmp);
std::unique_ptr<foo, custom_deleter> foo_ptr(tmp);
This is exception-safe for the same reason that std::unique_ptr<foo, custom_deleter> foo_ptr(new foo()); is exception-safe: unique_ptr guarantees that whatever you pass in to its constructor will eventually get deleted using the deleter.
Btw, doesn't custom_deleter need an operator()(foo*)? Or have I missed something?
Steve has already explained what the technical problem is, however, the underlying problem goes much deeper: The code employs an idiom helpful when you deal with naked pointers. Why does this code do two-step initialization (first create the object, then initialize it) in the first place? Since you want to use smart pointers, I'd suggest you carefully adapt the code:
foo* init_foo()
{
return new foo();
}
int main()
{
std::unique_ptr<foo, custom_deleter> foo_ptr( init_foo() );
}
Of course, renaming init_foo() to create_foo() and having it return a std::unique_ptr<foo> directly would be better. Also, when you use two-step initialization, it's often advisable to consider using a class to wrap the data.
You can use the following trick:
template<class T>
class ptr_setter
{
public:
ptr_setter(T& Ptr): m_Ptr{Ptr} {}
~ptr_setter() { m_Ptr.reset(m_RawPtr); }
ptr_setter(const ptr_setter&) = delete;
ptr_setter& operator=(const ptr_setter&) = delete;
auto operator&() { return &m_RawPtr; }
private:
T& m_Ptr;
typename T::pointer m_RawPtr{};
};
// Macro will not be needed with C++17 class template deduction.
// If you dislike macros (as all normal people should)
// it's possible to replace it with a helper function,
// although this would make the code a little more complex.
#define ptr_setter(ptr) ptr_setter<decltype(ptr)>(ptr)
and then:
std::unique_ptr<foo, custom_deleter> foo_ptr;
init_foo(&ptr_setter(foo_ptr));
I eventually came up with an approach that allows to initialise unique_ptr's with a code like this:
struct TOpenSSLDeleter { ... }; // Your custom deleter
std::unique_ptr<EVP_MD_CTX, TOpenSSLDeleter> Ctx;
...
Ctx = MakeUnique(EVP_MD_CTX_create()); // MakeUnique() accepts raw pointer
And here is the solution:
template <class X>
struct TUniquePtrInitHelper {
TUniquePtrInitHelper(X *Raw) noexcept {
m_Raw = Raw;
}
template <class T, class D>
operator std::unique_ptr<T, D>() const noexcept {
return std::unique_ptr<T, D>(m_Raw);
}
private:
X *m_Raw;
};
template <class X>
TUniquePtrInitHelper<X> MakeUnique(X *Raw) noexcept {
return {Raw};
}