I've been trying to expand a non value parameter pack recently in C++. Is this possible? And if it's not, why?
I mean, as you can see, in the line with the comment //, given a parameter pack for the TypeMap class, how can I call addType<T>() with each type of the parameter pack? Thanks in advance!
template <typename... T>
class TypeMap
{
using vari_cmps = std::variant<T*...>;
private:
template<typename Type>
void addType()
{
typemap[typeid(Type).name()] = std::make_unique<Type>(0).get();
}
public:
std::map<const char*, vari_cmps> typemap{};
TypeMap()
{
(addType<T,...>()); // Idk how to use this in order to make it work
}
~TypeMap()
{
typemap.clear();
}
};
As #HolyBlackCat has already answered in the comments, you can expand it like this:
TypeMap() {
(addType<T>(), ...);
}
If T is std::string, int, float this would expand to:
TypeMap() {
(addType<std::string>(), addType<int>(), addType<float>());
}
There are however a few more issues in this code-snippet:
1. addType()
addType() will not work as you'd expect, due to the unique_ptr deleteing your object after you put it into the map.
.get() only retrieves the pointer that the unique_ptr manages but does not transfer ownership, so the unique_ptr will still delete the pointed-to object once it gets out of scope, leaving a dangling pointer in your map.
so your addType() is roughly equivalent to:
template<typename Type>
void addType() {
Type* tptr = new Type(0); // unique pointer constructs object
typemap[typeid(Type).name()] = tptr; // insert pointer value of unique pointer
delete tptr; // unique pointer destructs
}
You could fix this by releasing the unique_ptr after inserting its value into the map & then cleaning it up in the destructor:
template<typename Type>
void addType() {
auto ptr = std::make_unique<Type>(0);
typemap[typeid(Type).name()] = ptr.get();
ptr.release(); // now unique_ptr won't delete the object
}
~TypeMap() {
// cleanup all pointers
for(auto& [name, ptrVariant] : typemap)
std::visit([](auto ptr) { delete ptr; }, ptrVariant);
}
2. Consider using std::type_index instead of const char* as map key
std::type_info::name() returns an implementation-defined name for the given type, so you have no guarantee that you will get an unique name for a given type.
Returns an implementation defined null-terminated character string containing the name of the type. No guarantees are given; in particular, the returned string can be identical for several types and change between invocations of the same program.
std::type_index on the other hand is build specifically for this purpose - using types as keys - and comes with all comparison operators & a std::hash specialization, so you can use it with std::map & std::unordered_map out of the box.
e.g.:
template <class... T>
class TypeMap
{
using vari_cmps = std::variant<T*...>;
private:
template<typename Type>
void addType()
{
typemap[std::type_index(typeid(Type))] = /* something */;
}
public:
std::map<std::type_index, vari_cmps> typemap{};
TypeMap() { /* ... */ }
~TypeMap() { /* ... */ }
template<class U>
U* get() {
auto it = typemap.find(std::type_index(typeid(U)));
return std::get<U*>(it->second);
}
};
Consider using std::tuple
std::tuple is basically built for this task, storing a list of arbitrary types:
e.g.:
template <class... T>
class TypeMap
{
private:
std::tuple<std::unique_ptr<T>...> values;
public:
TypeMap() : values(std::make_unique<T>(0)...) {
}
template<class U> requires (std::is_same_v<U, T> || ...)
U& get() { return *std::get<std::unique_ptr<U>>(values); }
template<class U> requires (std::is_same_v<U, T> || ...)
U const& get() const { return *std::get<std::unique_ptr<U>>(values); }
};
usage:
TypeMap<int, double, float> tm;
tm.get<int>() = 12;
If you want you can also store T's directly in the tuple, avoiding the additional allocations.
Related
std::deque<std::function<std::string()>> log_queue;
void store_log(T&& t, U&&... u) const
{
log_queue.push_back(
[t, u...]() {
return format_log(t, u...));
});
if (log_queue.size() > 1000)
{
log_queue.pop_front();
}
}
I use the above function to store the latest 1000 log messages in a circular buffer and print the contents of log_queue when an issue happens in my developing environment.
There is a problem with this approach. Some of the arguments (U...) are pointer wrappers.
So when I print log_queue contents, I see the last updated value for these pointers instead of the value it had when the store_log function was called. I can solve this issue by storing std::string instead of the lambda, but that is very very expensive (I want to stringify only the last 1000 logs). So instead I would like to store the value contained by the pointer wrappers
instead of the pointer wrapper itself. Is there any easy way?
Pointer wrapper details:
template <typename T>
class ptr_wrapper
{
T* t = nullptr;
int ref_count = 0;
// constructors and destructors
// functions for updating ref_count whenevr this object is copies/moved etc
};
};
You might apply transformation before the capture:
void store_log(T&& t, Us&&... us) const
{
log_queue.push_back(
[tup = std::make_tuple(value(t), value(us)...)]() {
return std::apply([](const auto&... args){ format_log(args...), tup));
});
if (log_queue.size() > 1000)
{
log_queue.pop_front();
}
}
with
template <typename T>
const T& value(const T& value) { return value; }
template <typename T>
std::optional<T> value(const ptr_wrapper<T>& wrapper) {
if (wrapper.t) return *wrapper.t;
return {};
}
// other overloads possible to do deep copy, instead of keeping reference
// ...
C++14's syntax; in C++11, you have to declare the variable before the lambda.
I have only recently started working with variadic templates.
Here is my problem:
I have a function called "addTo()" that adds a new object to a collection. The type of the object is the same as the type of the class the function resides in (its a template class). addTo() also uses a parameter pack to fill the constructor. addTo also recieves a key for the map (thats the collection). Now the collection stores the objects using a std::shared_ptr. Now to improve flexibility I want to add the ability to add a existing object to the collection, which will allow me to also add anything thats assignable.
here is an example (pseudo code)
class A{
//whatever
~~some function that can be overloaded
A(int,float);
}
class B:public A{
//whatever
~~overloads that function
}
//In some other function
AddToClass<A> instance;
instance.addTo(key,12,2.2); //this is how it already works
instance.addTo(key, new B(12,3.5)); //this is how I also want it to work
I dont know if this is achievable or how I can achieve this, however I beg you to explain the (possible) solution in detail since I still have loads of problems understanding parameter packs.
What I have tried
I tried using dynamic cast but I didnt manage to make it so that the
parameter pack gets expanded (because I don't really get how to use
them yet other than very basic function forwarding)
I tried just putting it in the std::make_shared call (the constructor) but that doesn't work because I don't want to force the types to have a constructor for each derived class (seems counter intuitive and bad practice)
Please explain to me how to do this (if its possible) in detail. It would really help me out a lot :)
---More info---
Here is an example of how the addToClass could look like (pseudo code, written from the top of my head):
template<class t>
class addToClass{
private:
std::map<key, shared_ptr<t>> collection;'
//sth else
public:
template<typename... Args>
void addTo(const Key &key, Args...args){
collection.insert(std::pair<key, shared_ptr<t>>(key,std::make_shared<t>(std::forward<Args>(args)...));
}
//other stuff
}
Now everything already works, all I want is that "addTo()" can also accept pointers of the type t or pointers to some class that derives from t. That would allow me to make the function a lot more flexible and save me loads of work in some cases.
If you want also that your client class can take pointers, you need SFINAE to disable your additional addTo function.
Full example:
class X {};
class A
{
public:
A( int, double) {}
A( X* ){}
};
class B: public A
{
public:
using A::A;
};
using Key = int;
template<class t>
class AddToClass
{
private:
std::map<Key, std::shared_ptr<t>> collection;
public:
template<typename... Args>
void addTo(const Key& key, Args...args)
{
collection.emplace( key, std::make_shared<t>(std::forward<Args>(args)...));
}
template < typename U, typename std::enable_if< std::is_base_of<t, U>::value, U>::type* = nullptr >
void addTo( const Key &key, U* ptr, X* = 0 )
{
collection.emplace( key, ptr );
}
};
int main()
{
AddToClass<A> instance;
Key key = 9;
instance.addTo(key,12,2.2); //this is how it already works
instance.addTo(key, new B{12,3.5}); //this is how I also want it to work
instance.addTo(key, new A{ new X});
instance.addTo(key, new B{ new X});
instance.addTo(key, new X);
}
If I understand correctly, your intention was to have a AddToClass<A>, not an AddToClass<int>.
In this case, I suggest to use emplace() instead of instert(), write your variadic method as follows
template <typename ... Args>
void addTo (Key const & key, Args && ... args)
{ collection.emplace(key,
std::make_shared<T>(std::forward<Args>(args)...)); }
and add a addTo() version for (derived from T) pointers
template <typename U>
void addTo (Key const & key, U * p)
{ collection.emplace(std::piecewise_construct,
std::forward_as_tuple(key),
std::forward_as_tuple(p)); }
If you want, you can add a std::enable_if to SFINAE enable the last method only if U is derived from T.
The following is a full working example
#include <map>
#include <memory>
struct A
{ A (int, float) {} };
struct B: public A
{ B (int i, float f) : A{i, f} {} };
using Key = int;
template <typename T>
class addToClass
{
private:
std::map<Key, std::shared_ptr<T>> collection;
public:
template <typename ... Args>
void addTo (Key const & key, Args && ... args)
{ collection.emplace(key,
std::make_shared<T>(std::forward<Args>(args)...)); }
template <typename U>
void addTo (Key const & key, U * p)
{ collection.emplace(std::piecewise_construct,
std::forward_as_tuple(key),
std::forward_as_tuple(p)); }
};
int main ()
{
addToClass<A> instance;
instance.addTo(1, 12,2.2);
instance.addTo(2, new B(12,3.5));
}
Many HDF5 functions are initialized as follows
hid_t handler = DoSomething(someHandler);
And one has to manually free the memory reserved by such an operation using something like:
freeme(handler);
So it's the same nightmare/problems that come along by using malloc and/or the new operator.
I want to create something like unique_ptr to handle this at destruction. The problem, however, that every different function has a different freeing function.
For example:
hid_t attribType = H5Aget_type(attribHandler);
must be freed with
H5Tclose(attribType);
But this function
attribHandler = H5Aopen(obj_id,"name",H5P_DEFAULT);
Must be freed with
H5Aclose(attribHandler);
So I need to write a class that can take hid_t as template parameter (that's easy), and also can take the freeing function as some kind of parameter, and call it on destruction.
What's the best way to achieve this?
Update
It was suggested to me to use std::unique_ptr with a custom deleter, but this doesn't work because std::unique_ptr expects a pointer.
std::unique_ptr<hid_t,std::function<herr_t(hid_t)>> attribType(H5Aget_type(attribHandler), [](hid_t f) { return H5Tclose(f); });
This creates a compile error due to the second parameter, the lambda function. The error says (g++ 4.9):
error: invalid conversion from ‘hid_t {aka int}’ to ‘std::unique_ptr<int, std::function<int(int)> >::pointer {aka int*}’ [-fpermissive]
std::unique_ptr<hid_t,std::function<herr_t(hid_t)>> attribType(H5Aget_type(attribHandler), [](hid_t f) { return H5Tclose(f); });
^
The error happens because std::unique_ptr expects hold a pointer to hid_t, not an hid_t object.
Is there a way around this? I think I can write my own class that does this now (I can use std::function to answer my first question), but would be great if I could use std::unique_ptr.
Something along these lines perhaps:
struct MyDeleter {
typedef hid_t pointer;
typedef void (*FreeFunc)(hid_t);
FreeFunc free_func_;
MyDeleter(FreeFunc free_func) : free_func_(free_func) {}
void operator()(hid_t f) const { free_func_(f); }
};
std::unique_ptr<hid_t, MyDeleter> p(
H5Aget_type(attribHandler),
MyDeleter(H5Tclose));
You may use something like:
template <typename Factory, Factory factory,
typename Deleter, Deleter deleter>
class SmartHandleH5;
template <typename Ret, typename ... Ts, Ret (*factory)(Ts...),
void (*deleter)(Ret)>
class SmartHandleH5<Ret (*)(Ts...), factory, void (*)(Ret), deleter>
{
public:
template <typename ... Us>
SmartHandle(Us&&... args) : handler(factory(std::forward<Us>(args)...)) {}
// Not copyable
SmartHandle(const SmartHandle&) = delete;
SmartHandle& operator =(const SmartHandle&) = delete;
// Not movable
SmartHandle(SmartHandle&&) = delete;
SmartHandle& operator =(SmartHandle&&) = delete;
// To avoid strange case with our template constructor
SmartHandle(SmartHandle&) = delete;
SmartHandle(const SmartHandle&&) = delete;
~SmartHandle() { deleter(handler); }
const T& get() const { return handler; }
T& get() { return handler; }
private:
Ret handler;
};
And then use the mapping factory/destructor once:
using SmartHandlerGetType = SmartHandlerH5<decltype(&H5Aget_type), H5Aget_type,
delctype(H5Tclose), H5Tclose>;
using SmartHandlerOpen = SmartHandlerH5<decltype(&H5Aopen), H5Aopen,
delctype(H5Aclose), H5Aclose>;
and use it:
SmartHandlerGetType attribType(attribHandler);
SmartHandlerOpen attribHandler(obj_id, "name", H5P_DEFAULT);
In addition, you might want to add an extra layer to hide completely hid_t
template <typename SmartHandle>
class HidHandle : private SmartHandle
{
public:
using SmartHandle::SmartHandle;
void foo() { someFunctionUsingHid(get()); }
};
and
using HidHandleGetType = HidHandle<SmartHandlerGetType>;
using HidHandleOpen = HidHandle<SmartHandlerOpen>;
I created my own handler class... it turned out it's not that difficult:
template <typename T, typename Deleter>
class SmartHandle
{
public:
typedef T value_type;
typedef Deleter deleter_function;
private:
T _value;
Deleter _deleter;
public:
SmartHandle(T value, Deleter deleter);
~SmartHandle();
T get();
};
template <typename T, typename Deleter>
SmartHandle<T,Deleter>::SmartHandle(T value, Deleter deleter)
{
this->_value = value;
this->_deleter = deleter;
}
template <typename T, typename Deleter>
T SmartHandle<T,Deleter>::get()
{
return _value;
}
template <typename T, typename Deleter>
SmartHandle<T,Deleter>::~SmartHandle()
{
_deleter(_value);
}
To use it:
SmartHandle<hid_t,std::function<herr_t(hid_t)>> attribType(H5Aget_type(attribHandler.get()), [](hid_t f) { return H5Tclose(f); });
Here attribHandler also uses a SmartHandle, which is why there's a .get() there.
Assume we have a data structure Foo that maintains a set of elements. It should be possible to associate attributes with the elements as needed. The attributes should be stored in a separate vector each. We implement this by means of variadic templates:
#include <vector>
template <typename ...Attrs>
struct Foo : public Attrs... {
Foo(int n = 0) {
using PackExpansionT = int[];
PackExpansionT{0, (Attrs::values.resize(n), 0)...};
}
};
struct AttrA { std::vector<int> values; };
struct AttrB { std::vector<float> values; };
struct AttrC { std::vector<double> values; };
int main() {
Foo<AttrA, AttrB> foo; // Maintains set of elements with two attributes each.
};
Now, I want a conversion operator with the following semantics:
Foo<AttrB, AttrC> bar = foo; // bar.AttrB::values should be a copy of foo.AttrB::values.
This is only an example. In general, the conversion operator should be able to convert a Foo with arbitrary attributes into another Foo with arbitrary attributes. Attributes associated with both Foos should be copied. Attributes not associated with both can be left defaulted. However, I have no idea how to implement it.
template <typename ...OthersAttrs>
operator Foo<OthersAttrs...>() const {
// ...?
}
We can just make a bunch of independent decisions. First, let's add a constructor so that we can construct Foo from its attribute constituents:
Foo(Attrs const&... attrs)
: Attrs(attrs)...
{ }
Next, for each attribute in Others, we will either downcast this to the appropriate type if possible or return a default-constructed one otherwise:
template <typename... Others>
operator Foo<Others...>() const {
return {get_attr<Others>(this)...};
}
where:
template <class T>
T const& get_attr(T const* v) const {
return *v;
}
template <class T>
T get_attr(...) const {
return T{};
}
An outline I can think of:
template <typename ...OthersAttrs>
operator Foo<OthersAttrs...>() const
{
Foo<OthersAttrs...> rv(GetNSomehow());
(int[]){(CopyAttr<Attrs>(&rv), 0)...};
return rv;
}
template<typename Attr>
void CopyAttr(Attr *rv) const // Overload A
{
rv->values = ((const Attr*)this)->values;
}
template<typename Attr>
void CopyAttr(...) const // Overload B
{
}
The trick here is to go attribute by attribute.
If rv has the attribute the first overload will be chosen and it will be copied.
Otherwise the second overload would be chosen that will do nothing.
I am using a class factory to create objects dynamically. I used this answer for its simplicity (and because I am using Qt).
But now I realize I must add an argument to my constructor
Item(bool newItem /* = true*/);
instead of
Item();
for the code in the referred answer:
template <typename T>
class ClassFactory
{
public:
template <typename TDerived>
void registerType(QString shape)
{
_createFuncs[shape] = &createFunc<TDerived>;
}
T* create(QString shape)
{
typename QMap<QString, PCreateFunc>::const_iterator it = _createFuncs.find(shape);
if (it != _createFuncs.end())
{
return it.value()();
}
return NULL;
}
private:
template <typename TDerived>
static T* createFunc()
{
return new TDerived();
}
typedef T* (*PCreateFunc)();
QMap<QString, PCreateFunc> _createFuncs;
};
I registered the class
classFactory.registerType <Type1_Item> ("type1");
when needed, I called
Item* item = classFactory.create("type1");
I am trying to add an additional argument in the class factory, to represent the constructor argument, but my attempts all result in error.
Why do I need it : simple case:
create a new object - sets defaults; for certain objects, it requires an open file dialog since data has to be loaded from a file.
load an object - fills data, including the filename for objects that contain file info
To be able to call the "load" function, an object must exist - which means that if I create a new object, I will trigger an open file dialog even though I do not need it.
The work around that I see is, to have a constructor followed by a setup function. But... that means constructing an object always requires a 2-function call, which seems like bad design.
that is why I am looking for a way to register and call the classes using simple calls like
classFactory.registerType <Type1_Item> ("type1", bool);
Item* item = classFactory.create("type1", true);
Is it possible, and how can I do it ?
The one way I can think of involves requiring that the arguments match exactly. First, we're going to store our functions using boost::any. This is because they may have different types, so we need a heterogenous container:
QMap<QString, boost::any> _createFuncs;
Our register function will create a specific function pointer to store in said any:
template <typename TDerived, typename... T>
void registerType(QString shape)
{
_createFuncs[shape] = &createFunc<TDerived, T...>;
}
where createFunc now takes extra arguments:
template <typename TDerived, typename... Args>
static T* createFunc(Args... args)
{
return new TDerived(args...);
}
The key is what we do on the creation side. We need to check to see if the any we have stored for the particular type is the right type:
template <typename... Args>
T* create(QString shape, Args... args)
{
using FPtr = T*(*)(Args...);
auto it = _createFuncs.find(shape);
if (it != _createFuncs.end())
{
// ok, it points to some any. is it the right any?
FPtr* fptr = boost::any_cast<FPtr>(&it.value());
if (fptr) {
return (*fptr)(args...);
}
// alternatively to the above, we can have createFunc
// throw bad_any_cast if you pass the wrong arguments
// which could be a loud, explicit failure rather than
// a silent one
return boost::any_cast<FPtr>(it.value())(args...);
}
return nullptr;
}
That will allow this to work:
classFactory.registerType<Item, bool>("type1");
^^^^^^
arg list
Item* item = classFactory.create("type1", true);
Item* item2 = classFactory.create<bool>("type1", 1);
But this will fail, since the any takes a bool, not an int:
Item* item3 = classFactory.create("type1", 1);
#Barry's answer is more than complete. However, if you are just interested in a simplified factory that can construct objects that have constructors taking different parameters, you can do something like:
// Factory method for classes having constructors
// that take an arbitary number of parameters
#include <memory>
class Factory
{
public:
template<typename T, typename... Params>
static std::unique_ptr<T> create(Params... params)
{
return std::make_unique<T>(params...);
}
};
struct Foo
{
Foo(int) {};
};
struct Bar
{
Bar(bool, double) {};
};
int main()
{
std::shared_ptr<Foo> foo = Factory::create<Foo>(42);
std::shared_ptr<Bar> bar = Factory::create<Bar>(true, 42.5);
}
Note that I used smart pointers here, so you don't need to keep track of new/deletes anymore.
You may use this modified version
template <typename T, typename ... Ts>
class ClassFactory
{
public:
template <typename TDerived>
void registerType(QString shape)
{
_createFuncs[shape] = &createFunc<TDerived>;
}
T* create(QString shape, Ts... args)
{
typename QMap<QString, PCreateFunc>::const_iterator it = _createFuncs.find(shape);
if (it != _createFuncs.end())
{
return it.value()(args...);
}
return nullptr;
}
private:
template <typename TDerived>
static T* createFunc(Ts... args)
{
return new TDerived(args);
}
typedef T* (*PCreateFunc)(Ts...);
QMap<QString, PCreateFunc> _createFuncs;
};
And use it
ClassFactory<Item, bool> classFactory;
classFactory.registerType <Type1_Item> ("type1");
Item* item = classFactory.create("type1", true);
If all the objects have the same parameter types (here a bool), just change the create function like this:
T* create(QString shape, bool param) //modified
{
typename QMap<QString, PCreateFunc>::const_iterator it = _createFuncs.find(shape);
if (it != _createFuncs.end())
{
return it.value()(param); //modified
}
return NULL;
}
And change createFunc also:
static T* createFunc(bool param)
{
return new TDerived(param);
}
typedef T* (*PCreateFunc)(bool);
I've done this using C++11 parameter packs:
// pack.cc
#include <utility>
template<class T, typename... P>
T * create(P&&... params)
{
return new T(std::forward<P>(params)...);
}
class X
{
public:
X() {}
};
class Y
{
public:
Y(int) {}
};
int main()
{
X * x = create<X>();
Y * y = create<Y>(1);
delete x;
delete y;
}
Compile this example g++ -std=c++11 -o pack pack.cc