create dense dynamic array (array of value) as library - c++

I tried to create packed array as a data structure for a game engine as described here:-
http://experilous.com/1/blog/post/dense-dynamic-arrays-with-stable-handles-part-1
In short, the structure stores values, instead of pointers.
Here is a draft.
template<class T> class Id{
int id;
}
template<class T> class PackArray{
std::vector <int>indirection ; //promote indirection here
std::vector <T>data;
//... some fields for pooling (for recycling instance of T)
Id<T> create(){
data.push_back(T());
//.... update indirection ...
return Id( .... index , usually = indirection.size()-1 .... )
}
T* get(Id<T> id){
return &data[indirection[id.id]];
//the return result is not stable, caller can't hold it very long
}
//... others function e.g. destroy(Id<T>) ...
}
The prototype works as I wished, but now I concern the beauty of old code.
For example, I had always created a new object like this:-
Bullet* bullet = new Bullet(gameEngine,velocity);
Now I must call :-
Id<Bullet> bullet = getManager()->create()->ini(velocity);
// getManager() usually return PackArray<Bullet>*
// For this data structure,
// if I want to hold the object for a long time, I have to cache it as Id.
Here are the questions :-
The new version of code is more ugly.
Should I avoid it? How to avoid it?
How to avoid / reduce programmer's-work of the above modification?
It is very tedious, when there are many of them scattering around.
(Edit) The scariest part is change in the type declaration e.g.
class Rocket{
std::vector<Bullet*> bullets;
//-> std::vector<Id<Bullet>> bullets;
void somefunction(){
Bullet* bullet = someQuery();
//-> Id<Bullet> bullet
}
}//These changes scatter around many places in many files.
This change (inserting the word "Id<>") means that the game logic has to know the underlying data structure that used to store Bullet.
If the underlying data structure would be changed again in future, I will have to manually refactor them one by one again (from Id<> to something else), i.e. lower maintainability.
(optional) What is the name of this data structure / technique?
As a library, should Id has a field of PackArray* to enable accessing the underlying object (e.g. Bullet*), without manager()?
Bullet* bullet = someId->getUnderlyingObject();

This behaviour of id sounds like handles, as in you don't give out information about the storage method, but guarantee access as long as the handle is valid. In the later respect handles behave like raw pointers: you won't be able to tell if it's valid (at least without the manager) and the handle might be reused at some point.
The question if changing from raw pointers to handles produces uglier code is very opinionated and I'd rather keep this objective: there's a balance between readably explicit and too much typing - everyone draws their own limits here. There's also advantages to having the calling site specify getManager: maybe there are multiple possible instances of these managers, maybe getting the manager requires locking and for multiple operations you want to lock only once. (You can support both of these cases in addition to what I present below.)
Let's use pointer/iterator notation to access the objects through our handles, reducing the amount of code changes necessary. Using std::make_unique and std::make_shared for reference, let's define make_handle to dispatch the creation to the right manager. I've adjusted PackArray::create a bit to make the following example more compact:
template<class T> class Handle;
template<class T> class PackArray;
template<class T, class... Args> Handle<T> make_handle(Args&&... args);
template<class T>
struct details {
friend class Handle<T>;
template<class U, class... Args> friend Handle<U> make_handle(Args&&... args);
private:
// tight control over who get's to access the underlying storage
static PackArray<T>& getManager();
};
template<class T>
class Handle {
friend class PackArray<T>;
size_t id;
public:
// accessors (via the manager)
T& operator*();
T* operator->() { return &*(*this); }
};
template<class T>
class PackArray {
std::vector<size_t> idx;
std::vector<T> data;
public:
template<class... Args>
Handle<T> create(Args&&... args) {
Handle<T> handle;
handle.id = data.size();
idx.push_back(data.size());
// enables non-default constructable types
data.emplace_back(std::forward<Args>(args)...);
return handle;
}
// access using the handle
T& get(Handle<T> handle) {
return data[idx[handle.id]];
}
};
template<class T, class... Args>
Handle<T> make_handle(Args&&... args) {
Handle<T> handle = details<T>::getManager().create(std::forward<Args>(args)...);
return handle;
}
template<class T>
T& Handle<T>::operator*() {
return details<T>::getManager().get(*this);
}
And the usage code would look like:
Handle<int> hIntA = make_handle<int>();
Handle<int> hIntB = make_handle<int>(13);
Handle<float> hFloatA = make_handle<float>(13.37f);
Handle<Bullet> hBulletA = make_handle<Bullet>();
// Accesses through the respective managers
*hIntA = 42; // assignment
std::cout << *hIntB; // prints 13
float foo = (*hFloatA + 12.26f) * 0.01;
applyDamage(hBulletA->GetDmgValue());
Every type needs a manager, i.e. if you don't define a default you'll get a compiler error. Alternatively you can provide a generic implementation (note: the initialisation of instance is not thread safe!):
template<class T>
PackArray<T>& details<T>::getManager() {
static PackArray<T> instance;
return instance;
}
You get special behaviour via template specialisation. You can even replace the manager type via template specialisation, allowing you to easily compare storage strategies (e.g. SOA vs. AOS).
template<>
struct details<Bullet> {
friend class Handle<Bullet>;
template<class U, class... Args> friend Handle<U> make_handle(Args&&... args);
private:
static MyBulletManager& getManager() {
static MyBulletManager instance;
std::cout << "special bullet store" << std::endl;
return instance;
}
};
And you can even make all of this const-correct (the same techniques as implementing custom iterators apply).
You may even want to extend the details<T> to a full traits type... It's all a balance between generalisation and complexity.

Related

Can static polymorphism (templates) be used despite type erasure?

Having returned relatively recently to C++ after decades of Java, I am currently struggling with a template-based approach to data conversion for instances where type erasure has been applied. Please bear with me, my nomenclature may still be off for C++-natives.
This is what I am trying to achieve:
Implement dynamic variables which are able to hold essentially any value type
Access the content of those variables using various other representations (string, ints, binary, ...)
Be able to hold variable instances in containers, independent of their value type
Convert between variable value and representation using conversion functions
Be able to introduce new representations just by providing new conversion functions
Constraints: use only C++-11 features if possible, no use of libraries like boost::any etc.
A rough sketch of this might look like this:
#include <iostream>
#include <vector>
void convert(const std::string &f, std::string &t) { t = f; }
void convert(const int &f, std::string &t) { t = std::to_string(f); }
void convert(const std::string &f, int &t) { t = std::stoi(f); }
void convert(const int &f, int &t) { t = f; }
struct Variable {
virtual void get(int &i) = 0;
virtual void get(std::string &s) = 0;
};
template <typename T> struct VariableImpl : Variable {
T value;
VariableImpl(const T &v) : value{v} {};
void get(int &i) { convert(value, i); };
void get(std::string &s) { convert(value, s); };
};
int main() {
VariableImpl<int> v1{42};
VariableImpl<std::string> v2{"1234"};
std::vector<Variable *> vars{&v1, &v2};
for (auto &v : vars) {
int i;
v->get(i);
std::string s;
v->get(s);
std::cout << "int representation: " << i <<
", string representation: " << s << std::endl;
}
return 0;
}
The code does what it is supposed to do, but obvoiusly I would like to get rid of Variable::get(int/std::string/...) and instead template them, because otherwise every new representation requires a definition and an implementation with the latter being exactly the same as all the others.
I've played with various approaches so far, like virtual templated, methods, applying the CRDT with intermediate type, various forms of wrappers, yet in all of them I get bitten by the erased value type of VariableImpl. On one hand, I think there might not be a solution, because after type erasure, the compiler cannot possibly know what templated getters and converter calls it must generate. On the other hand I think i might be missing something really essential here and there should be a solution despite the constraints mentioned above.
This is a classical double dispatch problem. The usual solution to this problem is to have some kind of dispatcher class with multiple implementations of the function you want to dispatch (get in your case). This is called the visitor pattern. The well-known drawback of it is the dependency cycle it creates (each class in the hierarchy depends on all other classes in the hierarchy). Thus there's a need to revisit it each time a new type is added. No amount of template wizardry eliminates it.
You don't have a specialised Visitor class, your Variable serves as a Visitor of itself, but this is a minor detail.
Since you don't like this solution, there is another one. It uses a registry of functions populated at run time and keyed on type identification of their arguments. This is sometimes called "Acyclic Visitor".
Here's a half-baked C++11-friendly implementation for your case.
#include <map>
#include <vector>
#include <typeinfo>
#include <typeindex>
#include <utility>
#include <functional>
#include <string>
#include <stdexcept>
struct Variable
{
virtual void convertValue(Variable& to) const = 0;
virtual ~Variable() {};
virtual std::type_index getTypeIdx() const = 0;
template <typename K> K get() const;
static std::map<std::pair<std::type_index, std::type_index>,
std::function<void(const Variable&, Variable&)>>
conversionMap;
template <typename T, typename K>
static void registerConversion(K (*fn)(const T&));
};
template <typename T>
struct VariableImpl : Variable
{
T value;
VariableImpl(const T &v) : value{v} {};
VariableImpl() : value{} {}; // this is needed for a declaration of
// `VariableImpl<K> below
// It can be avoided but it is
// a story for another day
void convertValue(Variable& to) const override
{
auto typeIdxFrom = getTypeIdx();
auto typeIdxTo = to.getTypeIdx();
if (typeIdxFrom == typeIdxTo) // no conversion needed
{
dynamic_cast<VariableImpl<T>&>(to).value = value;
}
else
{
auto fcnIter = conversionMap.find({getTypeIdx(), to.getTypeIdx()});
if (fcnIter != conversionMap.end())
{
fcnIter->second(*this, to);
}
else
throw std::logic_error("no conversion");
}
}
std::type_index getTypeIdx() const override
{
return std::type_index(typeid(T));
}
};
template <typename K> K Variable::get() const
{
VariableImpl<K> vk;
convertValue(vk);
return vk.value;
}
template <typename T, typename K>
void Variable::registerConversion(K (*fn)(const T&))
{
// add a mutex if you ever spread this over multiple threads
conversionMap[{std::type_index(typeid(T)), std::type_index(typeid(K))}] =
[fn](const Variable& from, Variable& to) {
dynamic_cast<VariableImpl<K>&>(to).value =
fn(dynamic_cast<const VariableImpl<T>&>(from).value);
};
}
Now of course you need to call registerConversion e.g. at the beginning of main and pass it each conversion function.
Variable::registerConversion(int_to_string);
Variable::registerConversion(string_to_int);
This is not ideal, but hardly anything is ever ideal.
Having said all that, I would recommend you revisit your design. Do you really need all these conversions? Why not pick one representation and stick with it?
Implement dynamic variables which are able to hold essentially any value type
Be able to hold variable instances in containers, independent of their value type
These two requirements are quite challenging on its own. The class templates don't really encourage inheritance, and you already did the right thing to hold what you asked for: introduced a common base class for the class template, which you can later refer to in order to store pointers of the said type in a collection.
Access the content of those variables using various other representations (string, ints, binary, ...)
Be able to introduce new representations just by providing new conversion functions
This is where it breaks. Function templates assume common implementation for different types, while inheritance assumes different implementation for the same types.
You goal is to introduce different implementation for different types, and in order to make your requirements viable you have to switch to one of those two options instead (or put up with a number of functions for each case which you have already introduced yourself)
Edit:
One of the strategies you may employ to enforce inheritance approach is generalisation of the arguments to the extent where they can be used interchangeably by the abstract interface. E.g. you may wrap the converting arguments inside of a union like this:
struct Variable {
struct converter_type {
enum { INT, STRING } type;
union {
int* m_int;
std::string* m_string;
};
};
virtual void get(converter_type& var) = 0;
virtual ~Variable() = default;
};
And then take whatever part of it inside of the implementation:
void get(converter_type& var) override {
switch (var.type) {
case converter_type::INT:
convert(value, var.m_int);
break;
case converter_type::STRING:
convert(value, var.m_string);
break;
}
}
To be honest I don't think this is a less verbose approach compared to just having a number of functions for each type combination, but i think you got the idea that you can just wrap your arguments somehow to cement the abstract class interface.
Implement std::any. It is similar to boost::any.
Create a conversion dispatcher based off typeids. Store your any alongside the conversion dispatcher.
"new conversion functions" have to be passed to the dispatcher.
When asked to convert to a type, pass that typeid to the dispatcher.
So we start with these 3 types:
using any = std::any; // implement this
using converter = std::function<any(any const&)>;
using convert_table = std::map<std::type_index, converter>;
using convert_lookup = convert_table(*)();
template<class T>
convert_table& lookup_convert_table() {
static convert_table t;
return t;
}
struct converter_any: any {
template<class T,
typename std::enable_if<
!std::is_same<typename std::decay<T>::type, converter_any>::value, bool
>::type = true
>
converter_any( T&& t ):
any(std::forward<T>(t)),
table(&lookup_convert_table<typename std::decay<T>::type>())
{}
converter_any(converter_any const&)=default;
converter_any(converter_any &&)=default;
converter_any& operator=(converter_any const&)=default;
converter_any& operator=(converter_any&&)=default;
~converter_any()=default;
converter_any()=default;
convert_table const* table = nullptr;
template<class U>
U convert_to() const {
if (!table)
throw 1; // make a better exception than int
auto it = table->find(typeid(U));
if (it == table->end())
throw 2; // make a better exception than int
any const& self = *this;
return any_cast<U>((it->second)(self));
}
};
template<class Dest, class Src>
bool add_converter_to_table( Dest(*f)(Src const&) ) {
lookup_convert_table<Src>()[typeid(Dest)] = [f](any const& s)->any {
Src src = std::any_cast<Src>(s);
auto r = f(src);
return r;
};
return true;
}
now your code looks like:
const bool bStringRegistered =
add_converter_to_table(+[](std::string const& f)->std::string{ return f; })
&& add_converter_to_table(+[](std::string const& f)->int{ return std::stoi(f); });
const bool bIntRegistered =
add_converter_to_table(+[](int const& i)->int{ return i; })
&& add_converter_to_table(+[](int const& i)->std::string{ return std::to_string(i); });
int main() {
converter_any v1{42};
converter_any v2{std::string("1234")};
std::vector<converter_any> vars{v1, v2}; // copies!
for (auto &v : vars) {
int i = v.convert_to<int>();
std::string s = v.convert_to<std::string>();
std::cout << "int representation: " << i <<
", string representation: " << s << std::endl;
}
}
live example.
...
Ok, what did I do?
I used any to be a smart void* that can store anything. Rewriting this is a bad idea, use someone else's implementation.
Then, I augmented it with a manually written virtual function table. Which table I add is determined by the constructor of my converter_any; here, I know the type stored, so I can store the right table.
Typically when using this technique, I'd know what functions are in there. For your implementation we do not; so the table is a map from the type id of the destination, to a conversion function.
The conversion function takes anys and returns anys -- again, don't repeat this work. And now it has a fixed signature.
To add support for a type, you independently register conversion functions. Here, my conversion function registration helper deduces the from type (to determine which table to register it in) and the destination type (to determine which entry in the table), and then automatically writes the any boxing/unboxing code for you.
...
At a higher level, what I'm doing is writing my own type erasure and object model. C++ has enough power that you can write your own object models, and when you want features that the default object model doesn't solve, well, roll a new object model.
Second, I'm using value types. A Java programmer isn't used to value types having polymorphic behavior, but much of C++ works much better if you write your code using value types.
So my converter_any is a polymorphic value type. You can store copies of them in vectors etc, and it just works.

Can I associate one class with another from a template (using C++17 variant)?

I have some code that accepts one type of object and creates another type of object based on the type of the first. (There is a 1->1 relationship between the types.) I originally used a hash table (unordered_map<>) with a key based on the type of the first object to associate a creation function for the second object. But as I am learning more about the C++ features introduced since the last time I was full-time C++, I discovered std::variant<>.
I have successfully converted the implementation to use this C++17 feature. However, there is one remaining piece that is still a bit cumbersome. The design makes a call to a static member function of the second class to validate the contents of the first object, before instantiating an object of the second class. To handle this right now, I'm using a visitor structure with function operators overloaded for each input type.
What I'm wondering is if there is some way to use a template for the association, rather than the copied code with only the types different?
I've tried looking at the way std::variant<> works, and I see where the index of the type can be obtained with .index(). I can see how to instantiate an object based on an index, which I might use if I created a second std::variant<> with the object types. But, as you can see, I don't want to instantiate the object until the parameters have been validated. The function that does that is static, and I don't see a way to associate the parms type with the object type in a way that lets me make the static call.
(I also realize that these two visitor structures can be combined in the code below, but in the real code, the creation is longer and more complicated, and I would rather not have copies of it in each overload.)
struct Type1Parms {};
struct Type2Parms {};
struct Type3Parms {};
...
struct TypeBase {};
struct Type1 : public TypeBase
{
static bool ValidateParms(const Type1Parms&);
Type1(const Type1Parms&);
};
struct Type2 : public TypeBase
{
static bool ValidateParms(const Type2Parms&);
Type2(const Type2Parms&);
};
struct Type3 : public TypeBase
{
static bool ValidateParms(const Type3Parms&);
Type3(const Type3Parms&);
};
...
struct ValidateParmsVisitor
{
bool operator()(const Type1Parms& parms)
{
return Type1::ValidateParms(parms);
}
bool operator()(const Type2Parms& parms)
{
return Type2::ValidateParms(parms);
}
bool operator()(const Type3Parms& parms)
{
return Type3::ValidateParms(parms);
}
...
};
using TypeParms = std::variant<Type1Parms, Type2Parms, Type3Parms, ...>;
struct CreateObjectVisitor
{
std::unique_ptr<TypeBase> operator()(const Type1Parms& parms)
{
return std::make_unique<Type1>(parms);
}
std::unique_ptr<TypeBase> operator()(const Type2Parms& parms)
{
return std::make_unique<Type2>(parms);
}
std::unique_ptr<TypeBase> operator()(const Type3Parms& parms)
{
return std::make_unique<Type3>(parms);
}
...
};
template<typename TParms>
std::unique_ptr<TypeBase> CreateType(const TParms& parms)
{
unique_ptr<TypeBase> obj;
if (visit(ValidateParmsVisitor{}, parms))
obj = visit(CreateObjectVisitor{}, parms);
return std::move(obj);
}
Is there a way to make this association, especially as a type that can be used with a static member function call?
EDIT: I should explain that this is part of a much larger project, with a number of other design criteria that shape its design.
For example, this is for a client interface, where the API is meant to be as simple as can be expressed. The client only has visibility (via header) to the parms structures and a function that takes the parms & returns an object that contains the objects mentioned above. The original design did indeed have a base structure for the parms, which obviously had to be in the public header. However, this meant that a client could inherit from the base class themselves and pass this into the object creation function, or inherit from the acceptable structures. To avoid segfaults, this necessitated adding runtime checks to be sure the types were acceptable, which was mostly handled by the hash design--although it wasn't quite that simple. When I removed the hash design, I also lost this method of type validation, but I recognized that this would be replaced by a compile time check with the variant<>, handling custom structures (no base to check now). I also learned about the C++ version of the final keyword which handled the inheritance issue.
Additionally, while the code above does not show it, the parms structures contain multiple members and the ValidateParms() functions actually attempt to validate whether the values and combinations are valid.
You can create traits for the association:
template <typename T> struct from_param;
template <> struct from_param<Type1Parms> { using type = Type1; };
template <> struct from_param<Type2Parms> { using type = Type2; };
template <> struct from_param<Type3Parms> { using type = Type3; };
Then, you might do
using TypeParms = std::variant<Type1Parms, Type2Parms, Type3Parms>;
std::unique_ptr<TypeBase> CreateType(const TypeParms& parms)
{
if (std::visit([](const auto& param){
return from_param<std::decay_t<decltype(param)>>::type::ValidateParms(parms);
}, parms))
{
return std::visit([](const auto& param) -> std::unique_ptr<TypeBase> {
return std::make_unique<typename from_param<std::decay_t<decltype(param)>>::type>(parms);
}, parms);
}
return nullptr;
}
Demo
or without variant, if you call with correct type:
template <typename T>
auto CreateType(const T& parms)
{
if (from_param<T>::type::ValidateParms(parms))
{
return std::make_unique<typename from_param<T>::type>(parms);
}
return nullptr;
}
There is a very simple method, a set of overloaded functions:
unique_ptr<TypeBase> CreateType(Type1Params const& params)
{
return make_unique<Type1>(params);
}
unique_ptr<TypeBase> CreateType(Type2Params const& params)
{
return make_unique<Type2>(params);
}
unique_ptr<TypeBase> CreateType(Type3Params const& params)
{
return make_unique<Type3>(params);
}
Notes:
You can add another overload to catch other parameters and then return null, but I think a compile-time error would be preferable.
You could also use a template function and specializations, but there's probably little typing to safe that way.

How to generalize this C++ wrapper around a C 'class'?

I am writing a C++ wrapper around a C library. Here is an example of my strategy.
// header file
class LibrdfUri { // wrapper around librdf.h librdf_uri*
/*
* If the deleter of std::unique_ptr is an empty
* class then it can do some optimizations and
* not actually store the deleter object.
* Otherwise it has to accommodate extra space for
* the deleter, which is unnecessary
* https://stackoverflow.com/questions/61969200/what-is-the-purpose-of-wrapping-this-private-deleter-function-in-a-struct/61969274#61969274
*/
struct deleter {
// turns deleter into a functor. For passing on to unique_ptr
void operator()(librdf_uri *ptr);
};
// automate management of librdf_uri* lifetime
std::unique_ptr<librdf_uri, deleter> librdf_uri_;
public:
LibrdfUri() = default;
explicit LibrdfUri(const std::string& uri); // construct from string
librdf_uri *get(); // returns the underlying raw pointer
};
// implementation
void LibrdfUri::deleter::operator()(librdf_uri *ptr) {
librdf_free_uri(ptr); // this is the C library function for destruction of librdf_uri
}
LibrdfUri::LibrdfUri(const std::string &uri) {
// create pointer to underlying C library 'object'
librdf_uri_ = std::unique_ptr<librdf_uri, deleter>(
librdf_new_uri(World::getWorld(), (const unsigned char *) uri.c_str()) // World::getWorld is static. Returns a pointer required by librdf_new_uri
);
}
librdf_uri *LibrdfUri::get() {
return librdf_uri_.get();
}
// and is used like so:
LibrdfUri uri("http://uri.com");
librdf_uri* curi = uri.get(); // when needed
This works for the single type librdf_uri* which is a part of the underlying library however I have lots of these. My question is double barrelled. The first part concerns the best general strategy for generalizing this wrapper to other classes while the second is concerns the implementation of that strategy.
Regarding the first part, here are my thoughts:
1. I could implement each class manually like I've done here. This is probably the simplest and least elegant. Yet it still might be my best option. However there is a small amount of code duplication involved, since each CWrapper I write will essentially have the same structure. Not to mention if I need to change something then I'll have to do each class individually.
2. Use an base class (abstract?)
3. Use a template
The second part of my question is basically: if I implement either option 2 or 3 (which I think might even be just a single option) how would I do it?
Here is a (vastly broken) version of what I'm thinking:
template<class LibrdfType>
class CWrapper {
struct deleter { ; //?
void operator()(LibrdfType *ptr) {
// ??
};
}
std::unique_ptr<LibrdfType, deleter> ptr;
public:
CWrapper() = default;
LibrdfType *get() {
ptr.get();
};
};
Then, LibrdfUri and any other C class I need to wrap, would just subclass CWrapper
This is a better deleter:
template<auto f>
using deleter=std::integral_constant< std::decay_t<decltype(f)>, f >;
use:
deleter<librdf_free_uri>
is a stateless deleter that calls librdf_free_uri.
But we don't need that I think. Here is what I might do:
There are 3 pieces of information you need.
How to construct
How to destroy
What type to store
One way is to define ADL baser helpers with famous names that you override to delete/construct.
template<class T>struct tag_t{};
template<class T>constexpr tag_t<T> tag{};
template<class T>
void delete_wrapptr(T*)=delete;
struct cleanup_wrapptr{
template<class T>
void operator()(T* t)const{ delete_wrapptr(t); }
};
template<class T>
using wrapptr=std::unique_ptr<T, cleanup_wrapptr>;
template<class T>
wrapptr<T> make_wrapptr( tag_t<T>, ... )=delete;
now you just have to write overloads for make and delete.
void delete_wrapptr(librdf_uri* ptr){
librdf_free_uri(ptr); // this is the C library function for destruction of librdf_uri
}
librdr_uri* make_wrapptr(tag_t<librdf_uri>, const std::string &uri) {
return librdf_new_uri(World::getWorld(), (const unsigned char *) uri.c_str()); // World::getWorld is static. Returns a pointer required by librdf_new_uri
}
and you can;
wrapptr<librdf_uri> ptr = make_wrapptr(tag<librdf_uri>, uri);
the implementation becomes just overriding those two functions.
make_wrapptr and delete_wrapptr overloads you write need to be visible at creating point, and in the namespace of T, tag_t or cleanup_wrapptr. The implementations can be hidden in a cpp file, but the declaration of the overload cannot.

unique_ptr deleter overhead

In normal C++ design, most objects can be deleted either by a delete statement, the free function, or a library-specific equivalent to free. For such objects, the unique_ptr Deleter implementation can be a stateless object that is eliminated through Empty Base Class Optimization. However, some libraries require using another object (which might contain a function pointer or some other context) to delete objects from that library.
typedef struct lib_object lib_object;
struct lib_api {
lib_object (*createInstance)();
void (*freeInstance)(lib_object *o);
};
One could wrap this in unique_ptr by storing a lib_api pointer as a data member in a custom Deleter, but if multiple lib_object instances needed to be managed, e.g. in a container, it would double the memory overhead of tracking the objects. What kind of pattern can be used to maintain RAII principles when dealing with this library, while still remaining memory efficient?
If there is only ever one lib_api object, then you can have your deleter get a static pointer to it.
If there can be more than one lib_api object then you have no choice but to store a pointer to it in the Deleter.
I use a custom deleter template for such objects.
template<typename T, T Function>
struct function_deleter
{
template<typename U>
auto operator()(U&& u) const noexcept(noexcept(Function(std::forward<U>(u))))
{
return Function(std::forward<U>(u));
}
};
then you can have use your deleter call free:
unique_ptr<int, function_deleter<void(*)(void*), &free>> uniq;
And its size is still equal to one pointer. live demo
Come C++17 you'll be able to use auto for non-type template parameters, simplifying the code to:
template<auto Function>
struct function_deleter
{
template<typename U>
auto operator()(U&& u) const noexcept(noexcept(Function(std::forward<U>(u))))
{
return Function(std::forward<U>(u));
}
};
and
unique_ptr<int, function_deleter<&call_free>> uniq;
live demo
Having this, in your case I'd keep a unique_ptr<pair<lib_object, lib_api>> with static deleter supporting this structure.
using lib_pair = pair<lib_object, lib_api>;
void lib_free(lib_pair* p){
p->second.freeInstance(p->first);
delete p;
}
using unique_lib_ptr = unique_ptr<lib_pair, function_deleter<void(*)(lib_pair*), &lib_free>>
This seems more cache-heavy but might just be your thing.
There should exist a more elegant solution but I would write something like
template <class ContainerType>
class TObjectContainer : public ContainerType {
public:
TObjectContainer() = default;
TObjectContainer(const TObjectContainer&); // should call createCopy
TObjectContainer(TObjectContainer&&) = default;
~TObjectContainer()
{ for (lib_object* element : *this)
(*freeInstance)(element);
}
private:
void (*freeInstance)(lib_object *o);
};
typedef TObjectContainer<std::vector<lib_object*>> ObjectVector;
It does not use unique_ptr but it should basically do the job.
Note that your are likely to overload every removal method like clear to call freeInstance or pop_back to return your original std::unique_ptr.

Generic container for multiple data types in C++

Using C++, I'm trying to create a generic container class to handle multiple data types. It's a common problem with a variety of solutions, but I've found nothing as... intuitive as I've grown accustomed to in languages like Python or even VB/VBA...
So here's my scenario:
I've built a DataContainer class based on boost::any which I use to store multiple data types of multiple elements. I use a map declared as:
std::map<std::string, DataContainer* (or DataContainerBase*)>
where DataContainer is a class that encapsulates an object of the type:
std::list<boost::any>
along with convenience functions for managing / accessing the list.
However, in the end, I'm still forced to do type conversions outside the data container.
For example, if I were to store a list of int values in the map, accessing them would require:
int value = boost::any_cast<int>(map["myValue"]->get());
I'd rather the boost code be contained entirely within the data container structure, so I would only need type:
int value = map["myValue"]->get();
or, worst-case:
int value = map["myValue"]->get<int>();
Of course, I could enumerate my data types and do something like:
int value = map["myValue"]->get( TYPE_INT );
or write type-specific get() functions:
getInt(), getString(), getBool() ...
The problem with the last two options is that they are somewhat inflexible, requiring me to declare explicitly each type I wish to store in the container. The any_cast solution (which I have implemented and works) I suppose is fine, it's just... inelegant? I dunno. It seems I shouldn't need to employ the internal mechanics externally as well.
As I see it, passing the value without declaring the value type in the call to the DataContainer member function would require a void* solution (which is undesirable for obvious reasons), and using a "get()" call would require (so far as I can tell) a "virtual template" member function defined at the base class level, which, of course, isn't allowed.
As it is, I have a workable solution, and really, my use in this case is limited enough in scope that most any solutions will work well. But I am wondering if perhaps there's a more flexible way to manage a generic, multi-type data container than this.
If you want to introduce some sugar for this:
int value = boost::any_cast<int>(map["myValue"]->get());
then you might want to make the get() function to return a proxy object, defined +- like this:
struct Proxy {
boost::any& value;
Proxy(boost::any& value) : value(value) {}
template<typename T>
operator T() {
return boost::any_cast<T>(value);
}
};
Then this syntax would work:
int value = map["myValue"]->get();
// returns a proxy which gets converted by any_cast<int>
However I recommend to keep things explicit and just use that syntax:
int value = map["myValue"]->get<int>();
Here get doesn't return a proxy object with a template method, but is a template method itself (but does the same as the template conversion operator shown above).
Today I have done some source code for the purpose you want. I know that this question is so old, but maybe this little piece of code is helpful for someone. It is mainly based on boost:any.
/*
* AnyValueMap.hpp
*
* Created on: Jun 3, 2013
* Author: alvaro
*/
#ifndef ANYVALUEMAP_HPP_
#define ANYVALUEMAP_HPP_
#include <map>
#include <boost/any.hpp>
using namespace std;
template <class T>
class AnyValueMap {
public:
AnyValueMap(){}
virtual ~AnyValueMap(){}
private:
map<T, boost::any> container_;
typedef typename map<T, boost::any>::iterator map_iterator;
typedef typename map<T, boost::any>::const_iterator map_const_iterator;
public:
bool containsKey(const T key) const
{
return container_.find(key) != container_.end();
}
bool remove(const T key)
{
map_iterator it = container_.find(key);
if(it != container_.end())
{
container_.erase(it);
return true;
}
return false;
}
template <class V>
V getValue(const T key, const V defaultValue) const
{
map_const_iterator it = container_.find(key);
if(it != container_.end())
{
return boost::any_cast<V>(it->second);
}
return defaultValue;
}
template <class V>
V getValue(const T key) const
{
return boost::any_cast<V>(container_.at(key));
}
template <class V>
void setValue(const T key, const V value)
{
container_[key] = value;
}
};
#endif /* ANYVALUEMAP_HPP_ */
A simple usage example:
AnyValueMap<unsigned long> myMap;
myMap.setValue<double>(365, 1254.33);
myMap.setValue<int>(366, 55);
double storedDoubleValue = myMap.getValue<double>(365);
int storedIntValue = myMap.getValue<int>(366);