Reputation: 1642
I use a library when many classes are written with a init() method returning boolean. I have read that it allow to return an error code. The signature have nothing common (the parameters can be anything) but the return type is almost always bool and the function name is always the same, init. The arguments can be anything.
Example:
class Handle {
Handle(const Handle&) = delete;
};
class BigResource {
BigResource(const BigResource&) = delete;
};
class A {
bool init(int, const BigResource&);
};
class B {
bool init(unsigned int, unsigned int, Handle&);
};
class D {
bool init();
}
class E {
bool init(const std::string&, const std::string&);
}
/* ... All classes are also not copyable */
The First Question is, that i would like to use these classes with container. A condition is that I assume that all call to init() should return true, not that they always return true but the program fails if any initialization does not work (unrecoverable error, like a bad malloc or asset not found). I tried to wrap all theses classes into another one that handle the initialization automatically, but this is not possible since the conversion operator does not work because there classes are not copyable.
So I managed to use std::generate to initialize the STL containers with a call to init() for each element. Now is the current problem: I would like to have a generic Generator class, or a functor, for initializing all the elements with the same argument that can handle any type respecting the reference arguments ? std::bind
pass by values, and I don't know if it's possible to use somethings like std::ref
with a variadic, with some arguments are passed by value and some by reference.
What I tried to do:
/**
* Functor to achieve the same effect but with a functor and stored arguments
*/
template<typename T> struct Creator
{
template<typename... Args>
Creator(Args&&... createArgs)
{
//m_callCreate = std::bind(create<T, Args...>, std::placeholders::_1, std::forward<Args>(createArgs)...); First test
m_callCreate = [&](T &obj) {
obj.init(std::forward<Args>(createArgs)...);
};
}
void operator()(T& t) {
m_callCreate(t);
}
private:
std::function<void(T&)> m_callCreate;
};
Does that work ? I feel like the lambda capture is resulting in a dangling reference. I tried also other possibilities (store a tuple, or a std::bind wrapped in a std::function that look like to wrap easely all arguments, but I don't know which method is better and even if it is not UB.) I don't understand deeply this functions and all possible behaviors, but in brief I would like to have the same behavior as if the init method was called directly, that is:
int
(the original object can be freed at the end of the constructor)Desired behavior:
// This code may be not possible
Handle handle;
std::vector<B> v(10, {1920, 1080, handle});
// Or at least:
Handle handle;
Generator<B> generator(1920, 1080, handle);
std::vector<B> v(10);
std::generate(v.begin(), v.end(), generator);
Upvotes: 0
Views: 83
Reputation: 132220
I tried to wrap all theses classes into another one that handle the initialization automatically, but this is not possible since the conversion operator does not work because there classes are not copyable.
Two things you could try in this respect:
class BigResource;
class A;
class MyA : A {
A wrapped;
public:
MyA() : wrapped() {
auto result = wrapped.init();
if (not result) { throw std::logic_error("Failed initializing A"); }
}
// more constructors
// etc. etc
}
BigResource
and Handle
Upvotes: 1