I have a template container class that needs to associate additional information with each instance of the user specified type.
The container specifies a wrapper structure that contains the specified type and the additional information...
template<typename T> class Container
{
...
struct Wrapper
{
T mType;
int mInfo;
};
}
On initialization, the container is populated with dynamically allocated instances of Wrapper (i.e., Wrapper*). The user can pop() an instance, and the container will return a cast to T*. The user can push() an instance, which the container will cast back to Wrapper*.
This works fine if the datatype can be default constructed. However, this is not always the case and I want to let the user provide an allocation function, invoked during initialization, that constructs the datatype however it likes. It's ok for the container to require a specific signature for the function.
template<typename T> template<typename F> void Container<T>::init(F allocator);
What I'm struggling with is how to allocate a Wrapper while the function allocates the datatype. I've thought about using placement new, where the container allocates enough space for Wrapper and passes the address to the function to do a placement new of the datatype.
Is there a cleaner way to go about this?
You do not need allocation function for this. Instead, you need something like emplace_push from standard containers.
I would just add the parameters used for construction of T as template parameters for Container, and use them to construct a T. To allow dynamic construction of the T objects, the container constructor accepts as parameter a pointer to a function taking as parameter an integer for the creation rank, and the default parameters for T constructor, and returning a tuple of parameter for T constructor (std::tuple<U...> (*getInitParam)(int rank, U... params)):
template<typename T, typename ...U>
class Container {
struct Wrapper {
T mType;
int mInfo;
Wrapper(int mInfo, U... params): mType(params...), mInfo(mInfo) {}
};
std::stack<Wrapper *> stack;
int get_mInfo() {
static int info = 0;
return info++;
}
std::tuple<U...> (*getInitParam)(int rank, U... params);
public:
T* pop() {
// should control stack is not empty
Wrapper *w = stack.top();
stack.pop();
return &w->mType;
}
void push(T* type) {
char *p = reinterpret_cast<char *>(type);
p -= offsetof(Wrapper, mType);
Wrapper *w = reinterpret_cast<Wrapper *>(p);
// would need to control that *w is a valid Wrapper
stack.push(w);
}
void init(int n, U... params) {
std::tuple<U...> orig = std::make_tuple(params...);
for (int i=0; i<n; i++) {
if (getInitParam != nullptr) {
std::tie(params...) = orig;
std::tuple<U...> tparams = (*getInitParam)(i, params...);
std::tie(params...) = tparams;
}
Wrapper *w = new Wrapper(get_mInfo(), params...);
stack.push(w);
}
}
Container(std::tuple<U...> (*getInitParam)(int rank, U... params) = nullptr)
: getInitParam(getInitParam) {}
~Container() {
while(! stack.empty()) {
Wrapper *w = stack.top();
delete w;
stack.pop();
}
}
};
Usage example:
std::tuple<size_t, char> doInit(int i, size_t szmin, char c) {
return std::make_tuple(szmin + i, c);
}
int main() {
Container<std::string, size_t, char> cont(&doInit);
cont.init(10, 5, 'x');
std::string* str0 = cont.pop();
std::string* str = cont.pop();
std::cout << *str0 << std::endl;
std::cout << *str << std::endl;
*str0 = "bar";
cont.push(str0);
cont.push(str);
str0 = cont.pop();
str = cont.pop();
std::cout << *str << std::endl;
cont.push(str);
cont.push(str0);
return 0;
}
This correctly outputs:
xxxxxxxxxxxxxx
xxxxxxxxxxxxx
bar
There is still to implement mInfo usage, but it should be a start point...
Related
Why can't I return a unique_ptr from a clone function? I thought I could do this.
I have a base class for different mathematical functions called transform. I have a container of pointers to this type because I am using polymorphism. For example, all these derived classes have a different implementation of log_jacobian that is useful for statistical algorithms.
I am using unique_ptrs to this transform class, and so I have made a (pure virtual) clone function that makes new unique_ptr pointing to a deep copy of the same mathematical transform object. This new object is of the same type that derives from transform<float_t>, but it's a separate because you can't have two unique_ptrs pointing to the same thing.
template<typename float_t>
class transform{
...
virtual std::unique_ptr<transform<float_t>> clone() const = 0;
...
};
My transform_container class holds a few of these at a time. After all, most statistical models have more than one parameter.
template<typename float_t, size_t numelem>
class transform_container{
private:
using array_ptrs = std::array<std::unique_ptr<transform<float_t>>, numelem>;
array_ptrs m_ts;
unsigned m_add_idx;
...
auto get_transforms() const -> array_ptrs;
};
I'm not sure why the deep copy function get_transforms isn't working, though. It is used to make copies, and to access individual transformations from the container. I get a segfault when I run some tests. If I run it in gdb, it explicitly tells me the line with a comment after it is bad.
template<typename float_t, size_t numelem>
auto transform_container<float_t,numelem>::get_transforms() const -> array_ptrs
{
array_ptrs deep_cpy;
for(size_t i = 0; i < numelem; ++i){
deep_cpy[i] = m_ts[i]->clone(); // this line
}
return deep_cpy;
}
I've also tried std::moveing it into deep_cpy[i] and using unique_ptr::reset, but to no avail.
Edit:
here are some other relevant methods: a method that adds transforms to transform_container, and the factory method for the individual transform:
template<typename float_t>
std::unique_ptr<transform<float_t> > transform<float_t>::create(trans_type tt)
{
if(tt == trans_type::TT_null){
return std::unique_ptr<transform<float_t> >(new null_trans<float_t> );
}else if(tt == trans_type::TT_twice_fisher){
return std::unique_ptr<transform<float_t> >(new twice_fisher_trans<float_t> );
}else if(tt == trans_type::TT_logit){
return std::unique_ptr<transform<float_t> >(new logit_trans<float_t> );
}else if(tt == trans_type::TT_log){
return std::unique_ptr<transform<float_t> >(new log_trans<float_t> );
}else{
throw std::invalid_argument("that transform type was not accounted for");
}
}
template<typename float_t, size_t numelem>
void transform_container<float_t, numelem>::add_transform(trans_type tt)
{
m_ts[m_add_idx] = transform<float_t>::create(tt);
m_add_idx++;
}
In get_transforms(), you are looping over the entire m_ts[] array, calling clone() on all elements - even ones that have not been assigned by add_transform() yet! Unassigned unique_ptrs will be holding a nullptr pointer, and it is undefined behavior to call a non-static class method through a nullptr.
The easiest fix is to change the loop in get_transforms() to use m_add_idx instead of numelem:
template<typename float_t, size_t numelem>
auto transform_container<float_t,numelem>::get_transforms() const -> array_ptrs
{
array_ptrs deep_cpy;
for(size_t i = 0; i < m_add_idx; ++i){ // <-- here
deep_cpy[i] = m_ts[i]->clone();
}
return deep_cpy;
}
Otherwise, you would have to manually ignore any nullptr elements, eg:
template<typename float_t, size_t numelem>
auto transform_container<float_t,numelem>::get_transforms() const -> array_ptrs
{
array_ptrs deep_cpy;
for(size_t i = 0, j = 0; i < numelem; ++i){
if (m_ts[i]) {
deep_cpy[j++] = m_ts[i]->clone();
}
}
return deep_cpy;
}
Either way, you should update add_transform() to validate that m_add_idx will not exceed numelem:
template<typename float_t, size_t numelem>
void transform_container<float_t, numelem>::add_transform(trans_type tt)
{
if (m_add_idx >= numelem) throw std::length_error("cant add any more transforms"); // <-- here
m_ts[m_add_idx] = transform<float_t>::create(tt);
++m_add_idx;
}
That being said, since transform_container can have a variable number of transforms assigned to it, I would suggest changing transform_container to use a std::vector instead of a std::array, eg:
template<typename float_t>
class transform_container{
private:
using vector_ptrs = std::vector<std::unique_ptr<transform<float_t>>>;
vector_ptrs m_ts;
...
auto get_transforms() const -> vector_ptrs;
};
template<typename float_t>
auto transform_container<float_t>::get_transforms() const -> vector_ptrs
{
vector_ptrs deep_cpy;
deep_cpy.reserve(m_ts.size());
for(const auto &elem : m_ts){
deep_cpy.push_back(elem->clone());
}
return deep_cpy;
}
template<typename float_t>
std::unique_ptr<transform<float_t>> transform<float_t>::create(trans_type tt)
{
switch (tt) {
case trans_type::TT_null:
return std::make_unique<null_trans<float_t>>();
case trans_type::TT_twice_fisher:
return std::make_unique<twice_fisher_trans<float_t>>();
case trans_type::TT_logit:
return std::make_unique<logit_trans<float_t>>();
case trans_type::TT_log:
return std::make_unique<log_trans<float_t>>();
}
throw std::invalid_argument("that transform type was not accounted for");
}
template<typename float_t>
void transform_container<float_t>::add_transform(trans_type tt)
{
m_ts.push_back(transform<float_t>::create(tt));
}
There are situations in which I need to pass a char* buffer back and forth. My idea is to create an object which can hold the object that owns the data, but also expose the data as char* for someone to read. Since this object holds the owner, there are no memory leaks because the owner is destructed with the object when it's no longer necessary.
I came with the implementation below, in which we have a segfault that I explain why it happens. In fact it's something that I know how to fix but it's something that my class kinda lured me into doing. So I consider what I've done to be not good and maybe there's a better way of doing this in C++ that is safer.
Please take a look at my class that holds the buffer owner and also holds the raw pointer to that buffer. I used GenericObjectHolder to be something that holds the owner for me, without my Buffer class being parametrized by this owner.
#include <iostream>
#include <string>
#include <memory>
#include <queue>
//The library:
class GenericObjectHolder
{
public:
GenericObjectHolder()
{
}
virtual ~GenericObjectHolder() {
};
};
template <class T, class Holder = GenericObjectHolder>
class Buffer final
{
public:
//Ownership WILL be passed to this object
static Buffer fromOwned(T rawBuffer, size_t size)
{
return Buffer(std::make_unique<T>(rawBuffer), size);
}
//Creates a buffer from an object that holds the buffer
//ownership and saves the object too so it's only destructed
//when this buffer itself is destructed
static Buffer fromObject(T rawBuffer, size_t size, Holder *holder)
{
return Buffer(rawBuffer, std::make_unique<T>(rawBuffer), size, holder);
}
//Allocates a new buffer with a size
static Buffer allocate(size_t size)
{
return Buffer(std::make_unique<T>(new T[size]), size);
}
~Buffer()
{
if (_holder)
delete _holder;
}
virtual T data()
{
return _rawBuffer;
}
virtual size_t size() const
{
return _size;
}
Buffer(T rawBuffer, std::unique_ptr<T> buffer, size_t size)
{
_rawBuffer = rawBuffer;
_buffer = std::move(buffer);
_size = size;
}
Buffer(T rawBuffer, std::unique_ptr<T> buffer, size_t size, Holder *holder)
{
_rawBuffer = rawBuffer;
_buffer = std::move(buffer);
_size = size;
_holder = holder;
}
Buffer(const Buffer &other)
: _size(other._size),
_holder(other._holder),
_buffer(std::make_unique<T>(*other._buffer))
{
}
private:
Holder *_holder;
T _rawBuffer;
std::unique_ptr<T> _buffer;
size_t _size = 0;
};
//Usage:
template <class T>
class MyHolder : public GenericObjectHolder
{
public:
MyHolder(T t) : t(t)
{
}
~MyHolder()
{
}
private:
T t;
};
int main()
{
std::queue<Buffer<const char*, MyHolder<std::string>>> queue;
std::cout << "begin" << std::endl;
{
//This string is going to be deleted, but `MyHolder` will still hold
//its buffer
std::string s("hello");
auto h = new MyHolder<std::string>(s);
auto b = Buffer<const char*, MyHolder<std::string>>::fromObject(s.c_str(),s.size(), h);
queue.emplace(b);
}
{
auto b = queue.front();
//We try to print the buffer from a deleted string, segfault
printf(b.data());
printf("\n");
}
std::cout << "end" << std::endl;
}
As you see, the s string is copied inside the object holder but gets destructed right after it. So when I try to access the raw buffer that buffer owns I get a segfault.
Of course I could simply copy the buffer from the s string into a new buffer inside my object, but It'd be inefficient.
Maybe there's a better way of doing such thing or maybe there's even something ready in C++ that does what I need.
PS: string is just an example. In pratice I could be dealing with any type of object that owns a char* buffer.
Live example: https://repl.it/repls/IncredibleHomelySdk
Your core problem is that you want your Holder to be moveable. But when the Owner object moves, the buffer object might also move. That will invalidate your pointer. You can avoid that by putting the owner in a fixed heap location via unique_ptr:
#include <string>
#include <memory>
#include <queue>
#include <functional>
template <class B, class Owner>
class Buffer
{
public:
Buffer(std::unique_ptr<Owner>&& owner, B buf, size_t size) :
_owner(std::move(owner)), _buf(std::move(buf)), _size(size)
{}
B data() { return _buf; }
size_t size() { return _size; }
private:
std::unique_ptr<Owner> _owner;
B _buf;
size_t _size;
};
//Allocates a new buffer with a size
template<typename T>
Buffer<T*, T[]> alloc_buffer(size_t size) {
auto buf = std::make_unique<T[]>(size);
return {std::move(buf), buf.get(), size};
}
Here's a repl link: https://repl.it/repls/TemporalFreshApi
If you want to have a type-erased Buffer, you can do that like this:
template <class B>
class Buffer
{
public:
virtual ~Buffer() = default;
B* data() { return _buf; }
size_t size() { return _size; }
protected:
Buffer(B* buf, size_t size) :
_buf(buf), _size(size) {};
B* _buf;
size_t _size;
};
template <class B, class Owner>
class BufferImpl : public Buffer<B>
{
public:
BufferImpl(std::unique_ptr<Owner>&& owner, B* buf, size_t size) :
Buffer<B>(buf, size), _owner(std::move(owner))
{}
private:
std::unique_ptr<Owner> _owner;
};
//Allocates a new buffer with a size
template<typename T>
std::unique_ptr<Buffer<T>> alloc_buffer(size_t size) {
auto buf = std::make_unique<T[]>(size);
return std::make_unique<BufferImpl<T, T[]>>(std::move(buf), buf.get(), size);
}
Again, repl link: https://repl.it/repls/YouthfulBoringSoftware#main.cpp
You wrote:
There are situations in which I need to pass a char* buffer back and
forth.
and
So I consider what I've done to be not good and maybe there's a better
way of doing this in C++ that is safer.
It's not exactly clear what you are aiming at, but when I have this need i will sometimes use std::vector<char> - a std::vector (and std::string) is a just that: a managed buffer. Calling data() on vector will give you a raw pointer to the buffer to pass on to legacy interfaces etc. or for whatever reason you just need a buffer that you manage yourself. Hint: use resize() or constructor to allocate the buffer.
So you see, there's no need to store the internal pointer of std::string in your example. Instead just call data() on a need basis.
It seems like you are concerned about copies and efficiency. If you use objects that support move semantics and you use the emplace family of functions there shouldn't be any copy-ing going on at least in c++17. All/most containers supports moving as well.
The class std::unique_ptr is already a "buffer holder" that "guarantee delete", no string copies, no dangling references and no seg faults:
#include <iostream>
#include <queue>
#include <memory>
int main()
{
std::queue<std::unique_ptr<std::string>> queue;
std::cout << "begin" << std::endl;
{
auto h = std::make_unique<std::string>("Hello");
queue.emplace( std::move(h) ); // move into the queue without copy
}
{
auto b = std::move(queue.front()); // move out from queue without copy
std::cout << *b << std::endl;
} // when b goes out of scope it delete the string
std::cout << "end" << std::endl;
}
https://godbolt.org/z/neP838
I'd like to simplify the code I write in my application that handles mutiple data structure types but with a common header. Given something like this:
enum class MyType {
Foo = 100,
Bar = 200,
};
struct Hdr {
MyType type;
};
struct Foo {
Hdr hdr;
int x;
int y;
int z;
};
struct Bar {
Hdr hdr;
double value;
double ratio;
};
void process(const Foo *ptr)
{
// process Foo here
}
void process(const Bar *ptr)
{
// process Bar here
}
extern void *getData();
int main()
{
const void *pv = getData();
auto pHdr = static_cast<const Hdr *>(pv);
switch (pHdr->type) {
case MyType::Foo: process(static_cast<const Foo *>(pv)); break;
case MyType::Bar: process(static_cast<const Bar *>(pv)); break;
default: throw "Unknown";
}
return 0;
}
Ideally I'd like to replace the switch statement above with something like:
process(multi_cast<pHdr->type>(pv);
I'm perfectly okay with having to write statements like this to get it to work:
template<MyType::Foo>
const Foo *multi_cast(void *p)
{
return static_cast<const Foo *>(p);
}
template<MyType::Bar>
const Bar *multi_cast(void *p)
{
return static_cast<const Bar *>(p);
}
But I cannot write a template where the template parameter is a enum (or an int for that matter)
Have I just looked at this for so long that I cannot see an answer?
Or is there just no other way to do it?
There is just no other way to do it.
As the comments have pointed out, since the type is stored in the header at run-time, you have to have some kind of run-time lookup; no amount of templates or overload resolution can help you since all of that is at compile-time.
You can abstract the lookup as much as you want, but you can only replace the switch statement with another type of lookup, and you can only decrease performance the further you get away from a simple switch/lookup table.
For example, you could start with something like this and go nuts:
#include <iostream>
#include <cassert>
enum class Type {
FOO,
BAR,
NUM_
};
struct Header {
Header(Type t)
: type(t)
{}
Type type;
};
struct Foo {
Foo(int x, int y, int z)
: header(Type::FOO), x(x), y(y), z(z)
{}
Header header;
int x;
int y;
int z;
};
struct Bar {
Bar(double value, double ratio)
: header(Type::BAR), value(value), ratio(ratio)
{}
Header header;
double value;
double ratio;
};
static inline void process(Foo*) {
printf("processing foo...\n");
}
static inline void process(Bar*) {
printf("processing bar...\n");
}
using ProcessFunc = void(*)(void*);
static ProcessFunc typeProcessors[(size_t)Type::NUM_] = {
[](void* p) { process((Foo*)p); },
[](void* p) { process((Bar*)p); },
};
static void process(void* p) {
Type t = ((Header*)p)->type;
assert((size_t)t < (size_t)Type::NUM_ && "Invalid Type.");
typeProcessors[(size_t)t](p);
}
static void* get_foo()
{
static Foo foo(0, 0, 0);
return &foo;
}
static void* get_bar()
{
static Bar bar(0.0, 0.0);
return &bar;
}
int main() {
Foo foo(0, 0, 0);
Bar bar(0.0, 0.0);
process(&foo);
process(&bar);
process(get_foo());
process(get_bar());
return 0;
}
but then you're only getting cute and most likely slower. You might as well just put the switch in process(void*)
If you aren't serializing your data(doubtful), are mostly processing one type at a time, and want an OO solution(I wouldn't), you could return a base type that your types inherit from and add a pure virtual process function like so:
struct Type {
virtual void process() = 0;
virtual ~Type() {}
};
struct Foo : Type {
int x = 0;
int y = 0;
int z = 0;
virtual void process() override {
printf("processing foo...\n");
}
};
struct Bar : Type {
double value = 0.0;
double ratio = 0.0;
virtual void process() override {
printf("processing bar...\n");
}
};
static Type* get_foo() {
static Foo foo;
return &foo;
}
static Type* get_bar() {
static Bar bar;
return &bar;
}
int main() {
Foo foo;
Bar bar;
foo.process();
bar.process();
get_foo()->process();
get_bar()->process();
return 0;
}
I would stick with the switch, but I would keep the values of Type::FOO and Type::BAR the default 0 and 1. If you mess with the values too much, the compiler might decide to implement the switch as a bunch of branches as opposed to a lookup table.
You have two issues:
Converting a runtime value (your "type") into a compile time determined type (with associated behavior).
"Unifying" the possible different types to a single (statically at compile time known) type.
Point 2 is what inheritance together with virtual member functions are for:
struct Thing {
virtual void doStuff() const = 0;
virtual ~Thing() {}
};
struct AThing : Thing {
void doStuff() const override { std::cout << "A"; }
};
struct BThing : Thing {
void doStuff() const override { std::cout << "B"; }
};
Point 1 is usually tackled by creating some kind of "factory" mechanism, and then dispatching based on the runtime value to one of those factories. First, the factories:
Thing * factoryA() { return new AThing(); }
Thing * factoryB() { return new BThing(); }
Thing * factory_failure() { throw 42; }
The "dispatching" (or "choosing the right factory") can be done in different ways, one of those being your switch statement (fast, but clumsy), linear search through some container/array (easy, slow) or by lookup in a map (logarithmic - or constant for hashing based maps).
I chose a (ordered) map, but instead of using std::map (or std::unordered_map) I use a (sorted!) std::array to avoid dynamic memory allocation:
// Our "map" is nothing more but an array of key value pairs.
template <
typename Key,
typename Value,
std::size_t Size>
using cmap = std::array<std::pair<Key,Value>, Size>;
// Long type names make code hard to read.
template <
typename First,
typename... Rest>
using cmap_from =
cmap<typename First::first_type,
typename First::second_type,
sizeof...(Rest) + 1u>;
// Helper function to avoid us having to specify the size
template <
typename First,
typename... Rest>
cmap_from<First, Rest...> make_cmap(First && first,
Rest && ... rest) {
return {std::forward<First>(first), std::forward<Rest>(rest)...};
}
Using std::lower_bound I perform a binary search on this sorted array (ehm "map"):
// Binary search for lower bound, check for equality
template <
typename Key,
typename Value,
std::size_t Size>
Value get_from(cmap<Key,Value,Size> const & map,
Key const & key,
Value alternative) {
assert(std::is_sorted(std::begin(map), std::end(map),
[](auto const & lhs, auto const & rhs) {
return lhs.first < rhs.first; }));
auto const lower = std::lower_bound(std::begin(map), std::end(map),
key,
[](auto const & pair, auto k) {
return pair.first < k; });
if (lower->first == key) {
return lower->second;
} else {
// could also throw or whatever other failure mode
return alternative;
}
}
So that, finally, I can use a static const map to get a factory given some runtime value "type" (or choice, as I named it):
int main() {
int const choices[] = {1, 10, 100};
static auto const map =
make_cmap(std::make_pair(1, factoryA),
std::make_pair(10, factoryB));
try {
for (int choice : choices) {
std::cout << "Processing choice " << choice << ": ";
auto const factory = get_from(map, choice, factory_failure);
Thing * thing = factory();
thing->doStuff();
std::cout << std::endl;
delete thing;
}
} catch (int const & value) {
std::cout << "Caught a " << value
<< " ... wow this is evil!" << std::endl;
}
}
(Live on ideone)
The initialization of that "map" could probably made constexpr.
Of course instead of raw pointers (Thing *) you should use managed pointers (like std::unique_ptr). Further, if you don't want to have your processing (doStuff) as member functions, then just make a single "dispatching" (virtual) member function that calls out to a given function object, passing the own instance (this). With a CRTP base class, you don't need to implement that member function for every one of your types.
You're using something that may be called static (=compile-time) polymorphism. This requires to make such switch statements in order to convert the run-time value pHrd->dtype to one of the compile-time values handles in the case clauses. Something like your
process(multi_cast<pHdr->type>(pv);
is impossible, since pHdr->type is not known at compile time.
If you want to avoid the switch, you can use ordinary dynamic polymorphism and forget about the enum Hdr, but use a abstract base class
struct Base {
virtual void process()=0;
virtual ~Base() {}
};
struct Foo : Base { /* ... */ };
struct Bar : Base { /* ... */ };
Base*ptr = getData();
ptr->process();
We have
enum Enum {A,B,C,D,E,F,G,H, NumEnums};
class Base {};
template <Enum...> class Thing : public Base {};
and the function
Base* create (std::list<Enum>& input);
is to create an object of the type that corresponds to input. For example,
if input = {A,E,C,G,D};, then the output shall be of type Thing<A,E,C,G,D>* (let's forget the sorting here). Now I know input is obtained during run-time, but by doing a search, the output can be obtained fairly quickly. If Thing had only one parameter (i.e. input has size() one), then the simple
template <int N>
Base* createHelper (const std::list<Enum>& input) {
const Enum En = static_cast<Enum>(N);
if (input.front() == En)
return new Thing<En>;
return createHelper<N+1>(input);
}
template <>
Base* createHelper<NumEnums> (const std::list<Enum>&) {
return nullptr;
}
Base* create (const std::list<Enum>& input) {
return createHelper<0>(input);
}
will do. I tried to generalize the above to any size list (the size would have to be determined during run-time through a similar recursion as above, but that should be fairly quick too). But I got totally lost on how. So I tried to examine the structure of the naïve method:
#include <iostream>
#include <list>
#include <type_traits>
#include <typeinfo>
enum Enum {A,B,C,D,E,F,G,H, NumEnums};
class Base {
public:
virtual void print() const = 0;
};
template <Enum...> class Thing : public Base {
virtual void print() const override {std::cout << typeid(*this).name() << '\n';}
};
Base* create (std::list<Enum>& input) {
if (input.front() == A) {
input.pop_front();
if (input.empty())
return new Thing<A>;
else {
if (input.front() == A) {
input.pop_front();
if (input.empty())
return new Thing<A,A>;
else {
// ....
}
}
else if (input.front() == B) {
input.pop_front();
if (input.empty())
return new Thing<A,B>;
else {
// ....
}
}
}
}
else if (input.front() == B) {
// similar
}
// ...
}
int main() {
std::list<Enum> userInput = {A,B};
// Wish to construct an instance of Thing<A,B> (efficiently).
Base* thing = create(userInput);
thing->print(); // Thing<A,B>
}
I figured I could put this in recursive form. But I cannot think of it. I know the one-dimensional case can be generalized, but I need help here. Or perhaps there is a better way to do it altogether? Once it works, it should not take anymore than a fraction of a second for the create function to return, assuming NumEnums is a decent size and the Thing class has just several template arguments, and not hundreds.
Edit: Turns out, there may be a viable solution here:
Create an associate array between your key and a type factory class.
Dynamically allocate any variables you may need from the type factory once you have it selected (preferably using std::unique_ptr).
The end result may end of looking like this:
std::unordered_map<std::string, type_allocator> str_to_type;
str_to_type["a"] = type_allocator(int); //where type_allocator derives the type of the class from the input variable.
auto variable = str_to_type[input].allocate();
For specific size, if you compute a single index, you may dispatch at runtime to the correct compile time function:
template <std::size_t N>
std::unique_ptr<Base> make_thing3()
{
constexpr Enum a2 = Enum(N % NumEnums);
constexpr Enum a1 = Enum((N / NumEnums) % NumEnums);
constexpr Enum a0 = Enum((N / NumEnums / NumEnums) % NumEnums);
return std::make_unique<Thing<a0, a1, a2>>();
}
template <std::size_t... Is>
std::unique_ptr<Base> make_thing3(std::size_t index, std::index_sequence<Is...>)
{
using maker = std::unique_ptr<Base>();
maker* fs[] = {&make_thing3<Is>...};
return fs[index]();
}
std::unique_ptr<Base> make_thing3(const std::array<Enum, 3u>& a)
{
std::size_t index = 0;
for (Enum e : a) {
index *= NumEnums;
index += e;
}
constexpr std::size_t total = NumEnums * NumEnums * NumEnums;
return make_thing3(index, std::make_index_sequence<total>{});
}
Live Demo
Note: I had to change size of Enum, and reduce my example from make_thing5 to make_thing3 due to compiler limit (not sure if it came from the site or if it is true limits)
This solution shows that though the compile-time is long (due to the many template instantiations), the run-time look-up is instant. The compiler limits is 3 enum values as input though. The empty input case is handled too (the return type being Thing<>*).
#include <iostream>
#include <list>
#define show(variable) std::cout << #variable << " = " << variable << std::endl;
enum Enum {A,B,C,D,E,F,G,H, NumEnums};
class Base {
public:
virtual void print() const = 0;
};
template <Enum... Es> class Thing : public Base {
virtual void print() const override {
const std::list<int> a = {((std::cout << Es << ' '), 0)...};
std::cout << "\nPack size = " << sizeof...(Es) << '\n';
}
};
template <int N, int Size, Enum... Es>
struct Create {
static Base* execute (std::list<Enum>& input) {
const Enum En = static_cast<Enum>(N);
if (input.front() == En) {
input.pop_front();
return Create<0, Size-1, Es..., En>::execute(input);
}
return Create<N+1, Size, Es...>::execute(input);
}
};
template <int N, Enum... Es>
struct Create<N, 0, Es...> {
static Base* execute (std::list<Enum>&) {return new Thing<Es...>;}
};
template <int Size, Enum... Es>
struct Create<NumEnums, Size, Es...> {
static Base* execute (std::list<Enum>&) {return nullptr;} // This will never be reached
};
template <int Size>
Base* do_create (std::list<Enum>& input) {
if (input.size() == Size)
return Create<0, Size>::execute(input);
return do_create<Size+1>(input);
}
template <>
Base* do_create<4> (std::list<Enum>&) {
std::cout << "Cannot exceed 3 values.\n";
return nullptr;
}
Base* create (std::list<Enum>& input) {
return do_create<0>(input);
}
int main() {
std::list<Enum> input = {E,A,F};
Base* thing = create(input);
thing->print(); // 4 0 5
input = {};
create(input)->print(); // Pack size = 0.
}
I am creating a Lua api for my program.
I want to be able to do the following:
void* CreateA()
{
A* a = new A();
PointerTypes[a] = A;
return reinterpret_cast<void*>(a);
}
void* CreateB()
{
B* b = new B();
PointerTypes[b] = B;
return reinterpret_cast<void*(b);
}
void DeleteThing( void* p )
{
typename type = PointerTypes[p];
type* t = reinterpret_cast< type >( p );
delete t;
}
Is there any straightforward way to do this?
PS: My application already uses RTTI so it can be used here too.
Instead of saving the type in a map (which is not possible because types aren't first class objects in C++) you could store a deleter function in a map. Your factory function then becomes:
void* CreateA()
{
A *a = new A();
PointerDeleters[a] = [](void *obj) { delete static_cast<A*>(obj); };
return a;
}
or with a function template:
template<typename T>
void* Create() // maybe you want to forward c'tor args using variadic template
{
T *a = new T();
PointerDeleters[t] = [](void *obj) { delete static_cast<T*>(obj); };
return t;
}
And then invoke it to trigger the deletion of the object p with unknown type:
void DeleteThing(void* p)
{
PointerDeleters[p]();
}
The map PointerDeleters should then have the value type std::function<void(void*)>.
A better solution would be (if your design allows it) to use a base class with a virtual destructor; then you can simply delete a pointer to that class without storing any additional information:
template<typename T>
BaseClass* Create()
{
return new T();
}
void DeleteThing(BaseClass* p)
{
delete p;
}