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.
Related
How do ask the compiler if a type has a move constructor?
std::is_move_constructible doesn't apply because it will return true even if the type is copy constructible only.
Is there something that will return whether or not there is actually a user-defined move constructor?
EDIT:
For background, I am trying to create a type erased class that will (given a set of types) decide whether or not it is worth saving off a type erased version of the move constructor.
If at least one of these types does have an "interesting" move constructor (non trivial and actually implemented), then it should save off the move constructor, else it won't and will save space
#include <type_traits>
struct NoMove {
NoMove(NoMove const&) {}
//NoMove(NoMove&&) = delete;
};
struct InterestingMove {
InterestingMove(InterestingMove&&) {}
};
template<typename T>
struct is_interesting_move_constructor :
std::integral_constant<bool,
std::is_move_constructible<T>::value &&
!std::is_trivially_move_constructible<T>::value> {};
// Fails:
//static_assert(!is_interesting_move_constructor<NoMove>::value);
// Is fine:
static_assert(is_interesting_move_constructor<InterestingMove>::value);
The problem with this attempt is that it should return false on NoMove but it doesn't because is_move_constructible doesn't return true based on whether or not there is actually a move constructor
Type erased class sketch (think like a std::any but limited to a set of types):
template<typename... Ts> // One of these types will be held
struct type_eraser_thing
{
void* obj; // pointer to actual thing
std::function<void*(void*,void*)> move_constr;
std::function<void*(void*,void const*)> copy_constr;
// Plus other goodies not shown
std::size_t size;
template<typename T>
type_eraser_thing(T const& other)
{
// static_assert to make sure T is in Ts...
// Get the memory
obj = malloc(sizeof(T));
obj = new (obj) T(other);
size = sizeof(T);
// Save the move_constructor
move_constr = [](void* dest, void* src){
return new (dest) T(std::move(*reinterpret_cast<T*>(src)));
};
}
// Other stuff (copy constructor, etc)
//
// The real problem the move constructor:
type_eraser_thing(T&& other)
{
obj = malloc(other.size);
obj = other.move_constr(obj,other.obj);
obj.move_constr = other.move_constr;
other.obj = nullptr;
}
};
In the implementation of the move constructor, it doesn't make sense to have a call to a std::function (not really, but something similar) if the move constructor doesn't do anything, so I want to SFINAE it out if it doesn't do anything
The concept you seem to be looking for is:
template <typename T>
concept C = not std::is_trivially_move_constructible_v<T>;
Here's a demo.
If you don't have c++20, you can write this trait yourself, or just use the right hand side where needed, since it's pretty short.
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();
Consider polymorphic classes with a base object, a derived interface, and a final object:
// base object
struct object
{
virtual ~object() = default;
};
// interfaces derived from base object
struct interface1 : object
{
virtual void print_hello() const = 0;
template<typename T>
static void on_destruction(object* /*ptr*/)
{
std::cout << "interface1::on_destruction" << std::endl;
}
};
// final object
struct derived1 : interface1
{
virtual void print_hello() const override
{
std::cout << "hello" << std::endl;
}
static std::string get_type_name()
{
return "derived1";
}
};
In the real use case, final objects are defined through a plugin system, but that is not the point. Note that I want to be able to call on_destruction when an object is destroyed (see register_object below). I want to use these classes as follows:
int main()
{
// register derived1 as an instantiable object,
// may be called in a plugin
register_object<derived1>();
// create an instance using the factory system
auto instance = create_unique<interface1>("derived1");
instance->print_hello();
return 0;
}
Using std::unique_ptr to manage the objects, I ended up with the following code for register_object:
template<typename T>
using unique = std::unique_ptr<
T,
std::function<void(object*)> // object deleter
>;
namespace
{
std::map< std::string, std::function<unique<object>(void)> > factory_map;
}
template<typename T>
void register_object()
{
factory_map.emplace(
T::get_type_name(),
[]()
{
unique<T> instance{
new T,
[](object* ptr)
{
T::on_destruction<T>(ptr);
delete ptr;
}
};
return static_move_cast<object>(
std::move(instance)
);
}
);
}
And the create* functions:
unique<object> create_unique_object(const std::string& type_name)
{
auto f = factory_map.at(type_name);
return f();
}
template<typename T>
unique<T> create_unique(const std::string& type_name)
{
return static_move_cast<T>(
create_unique_object(type_name)
);
}
You noticed in register_object and create_unique the call to static_move_cast, which is declared as:
template<typename U, typename T, typename D>
std::unique_ptr<U, D>
static_move_cast
(
std::unique_ptr<T, D>&& to_move_cast
)
{
auto deleter = to_move_cast.get_deleter();
return std::unique_ptr<U, D>{
static_cast<U*>(
to_move_cast.release()
),
deleter
};
}
The goal behind static_move_cast is to allow static_cast on std::unique_ptr while moving the deleter during the cast. The code is working, but I feel like hacking std::unique_ptr. Is there a way to refactor the code to avoid my static_move_cast?
static_move_cast is unnecessary within register_object, since you can just use the converting constructor of unique_ptr template< class U, class E > unique_ptr( unique_ptr<U, E>&& u ):
unique<T> instance{
new T,
// ...
};
return instance;
Or, even simpler, construct and return a unique<object> directly, since T* is convertible to object*:
return unique<object>{
new T,
// ...
};
However for create_unique the use of static_move_cast is unavoidable, since the converting constructor of unique_ptr won't work for downcasts.
Note that shared_ptr has static_pointer_cast, which performs downcasts, but there is no corresponding facility for unique_ptr, presumably because it it is considered straightforward and correct to perform the cast yourself.
I would say it is good solution given the requirements. You transfer the responsibility to the caller of create_unique. He must give correct combination of type and string and string that is in the registry.
auto instance = create_unique<interface1>("derived1");
// ^^^^^^^^^^ ^^^^^^^^
// what if those two don't match?
You could improve it a bit by changing the static_cast to dynamic_cast. And the caller of create_unique should always check that he got non-null pointer before calling anything on it.
Or at least use dynamic_cast with assert in debug mode, so you catch mismatches while developing.
Alternative refactoring: Have separate factory for every existing interface.
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).
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};
}