I have a class like this:
class factory;
using factory_ptr = std::unique_ptr<IComponent> (factory::*)() const noexcept;
class factory {
public:
factory();
~factory() = default;
std::unique_ptr<Chipset> &create(const std::string &type);
private:
template<class T>
std::unique_ptr<T> Tcreate() const noexcept;
std::map<std::string, factory_ptr> m_fac;
};
#include "factory.inl"
My template function Tcreate is just:
template<class T>
std::unique_ptr<T> factory::Tcreate() const noexcept {
return std::make_unique<T>();
}
And the other function are just:
factory::factory() {
m_fac.emplace("4001", &factory::Tcreate<chipset4001>);
m_fac.emplace("4008", &factory::Tcreate<chipset4008>);
m_fac.emplace("4011", &factory::Tcreate<chipset4011>);
m_fac.emplace("4030", &factory::Tcreate<chipset4030>);
m_fac.emplace("4069", &factory::Tcreate<chipset4069>);
m_fac.emplace("4071", &factory::Tcreate<chipset4071>);
m_fac.emplace("4081", &factory::Tcreate<chipset4081>);
m_fac.emplace("4512", &factory::Tcreate<chipset4512>);
}
std::unique_ptr<Chipset> &factory::create(const std::string &type) {
if (m_fac.find(type) == m_fac.end()) {
throw nts::exception("can't find the chipset: " + type, "FactoryCreate");
}
return (this->*(m_fac.find(type)->second))();
}
Every chipset like chipsetXXXX are a class like:
class chipsetXXXX : Chipset {}
What I want to do here with this code is to generate an std::unique_ptr<> of a certain chipset linked with a string (cf. factory::m_fac), but when I run it a lot of error message pop on my terminal (more than what my terminal can handle). but i can't figured out what go wrong with it.
The issue is that your Tcreate function does not have the required signature. You're trying to create a map of functions which return an std::unique_ptr<IComponent>, but Tcreate() returns std::unique_ptr<T>.
I'm assuming Chipset inherits from IComponent. And as you note each T inherits from Chipset. So the conversion from e.g. std::unique_ptr<chipset4001> to std::unique_ptr<IComponent> is certainly possible, but that doesn't mean that the signature matches. E.g. a pointer to a function double do_thing () can't be assigned to a function pointer expecting an int (*) ().
So the solution is to change the return type of Tcreate to std::unique_ptr<IComponent>:
template<class T>
std::unique_ptr<IComponent> factory::Tcreate() const noexcept {
return std::make_unique<T>();
}
However, when you do that, you'll now get a compile error in create(), because that tries to return an std::unique_ptr<Chipset>. It's up to you to decide what to do there. Either return std::unique_ptr<IComponent>, or change factory_ptr to be a pointer to a function returning std::unique_ptr<Chipset> (and of course change Tcreate() accordingly).
Related
So guys, I have an abstract class, other class that stores an implementation from this class in the stack (I don't want heap allocations and I don't know other way to do it without making the caller explicitly declares the implementation) and another that stores a reference of this interface class. But, it seems that GCC don't store the implementation class in the stack and when the interface class is used probably the implementation class vtable is not found.
Basically, everything works fine when compiled with GCC 4.8.1 without optimizations, but when I try to use it, the program crashes and then returns 139.
I don't know why GCC 4 doesn't support it, while GCC 5 does, but I see that they generate different instructions.
Compiler Explorer: https://godbolt.org/z/Wfvj65
#include <cstdio>
#define FORCEINLINE inline __attribute__((always_inline))
class IFormatter
{
public:
virtual void Format(const void* InData) const = 0;
};
template<typename T>
class TFormatter :
public IFormatter
{
public:
TFormatter() = delete;
};
using Scalar = float;
// Implemented, fine.
struct RVector2
{
Scalar X;
Scalar Y;
};
// Not implemented, get error.
struct RVector3
{
Scalar X;
Scalar Y;
Scalar Z;
};
template<>
class TFormatter<RVector2> :
public IFormatter
{
public:
virtual void Format(const void*) const override
{
printf("[RVector2]\n");
}
};
template<typename T>
class TCustom
{
public:
FORCEINLINE TCustom(const T& InValue) :
Value(InValue),
Format(TFormatter<T>{})
{
}
FORCEINLINE const T* Data() const
{
return &Value;
}
FORCEINLINE const IFormatter& Formatter() const
{
return Format;
}
private:
const T& Value;
TFormatter<T> Format;
};
template<typename T>
FORCEINLINE TCustom<T> MakeCustom(const T& InValue)
{
return TCustom<T>{ InValue };
}
class RCustom
{
public:
FORCEINLINE RCustom(const void* InValue, const IFormatter& InFormatter) :
Data(InValue),
Formatter(InFormatter)
{
}
template<typename T>
FORCEINLINE RCustom(TCustom<T> const& InCustom) :
RCustom(InCustom.Data(), InCustom.Formatter())
{
}
FORCEINLINE const IFormatter& Get() const
{
return Formatter;
}
private:
const void* Data;
const IFormatter& Formatter;
};
int main()
{
const RVector2 Vector{};
const RCustom Custom = MakeCustom(Vector);
Custom.Get().Format(nullptr);
return 0;
}
As one of the comments said there is something weird going on with storing TCustom in the unrelated type RCustom. The implicit constructor RCustom(TCustom) threw me off.
The issue is quite subtle. If something works with -O0 but not with -Ofast (or -O2/-O3), most of the time something funny is happening with the memory. As Benny K said, in your case the issue is that RCustom only stores a reference to IFormatter:
class RCustom {
...
const IFormatter& Formatter; // Asking for problems
}
This is seems like an innocent &, but in fact this is dangerous. Because the validity of this member is dependent on the lifetime of an external object. There are a few possibilities to fix this. You could save a copy of the TFormatter in RCustom (instead of a reference):
template<typename T>
class RCustom {
...
const TFormatter<T> Formatter;
}
But this also means you have to give up the abstract interface IFormatter for the concrete one TFormatter<T>. To work with virtual methods in C++ you need a pointer, but using a raw-pointer will introduce the same memory problems as the references. So I suggest you use smart pointers:
class RCustom {
...
std::shared_ptr<const IFormatter> Formatter;
}
PS: to be precise about what's going wrong: In MakeCustom() you initialize a TCustom object which initializes and copies an instance of TFormatter. Next a reference to the instance of TFormatter in TCustom is saved in RCustom. Now this RCustom object is returned and the function MakeCustom() is cleaned up. In this cleaning process TCustom is destroyed, and so is the TFormatter-member. But the RCustom still retains a reference to this invalid memory. In C++ the difference between an & and no & is rather important.
I'm not sure how best to phrase the question, but I'm not asking how to implement templated virtual functions per-se. I'm building an entity component system, and I have two important classes - World and Entity. World is actually an abstract class, and the implementation (let's call it WorldImpl) is a templated class that allows use of a custom allocator (one that can be used with std::allocator_traits).
Components are any data type which we can attach to entities. This is done by calling a templated function named assign on the entity.
Here's the problem: I'm trying to make the entity use the world's allocator when creating and initializing components. In a perfect world, you would call Entity::assign<ComponentType>( ... ) which would ask the WorldImpl to create the component with whatever allocator is appropriate. There's a problem here, however - The entity has a pointer to World and templated virtual functions aren't possible to my knowledge.
Here's a bit more of an illustration that might make the issue more obvious:
class Entity
{
template<typename ComponentType>
void assign(/* ... */)
{
/* ... */
ComponentType* component = world->createComponent<ComponentType>(/* ... */);
/* ... */
}
World* world;
};
// This is the world interface.
class World
{
// This is the ideal, which isn't possible as it would require templated virtual functions.
template<typename ComponentType>
virtual ComponentType* createComponent(/* ... */) = 0;
};
template<typename Allocator>
class WorldImpl : public World
{
template<typename ComponentType> // again, not actually possible
virtual ComponentType* createComponent(/* ... */)
{
// do something with Allocator and ComponentType here
}
};
Seeing as the above code isn't actually possible, here's the real question: With a class hierarchy such as this, what black magic do I have to do in order for some function to be called with both the ComponentType and Allocator template parameters? This is the ultimate goal - a function called on some object with both template parameters available to it.
I'd say that Entities belong to a certain kind of world and make them templates with a World parameter. Then you can forget about all the inheritance and virtual and just implement worlds that fulfill the required interface, e.g.
template<typename World>
class Entity
{
template<typename ComponentType>
void assign(/* ... */)
{
/* ... */
ComponentType* component = world.createComponent<ComponentType>(/* ... */);
/* ... */
}
World world;
};
template<typename Allocator>
class WorldI
{
template<typename ComponentType>
ComponentType* createComponent(/* ... */)
{
// do something with Allocator and ComponentType here
}
};
Note that this isn't an optimal solution (see the bottom of the post for issues), but a somewhat-viable way to combine templates and virtual functions. I post it in the hopes that you can use it as a basis to come up with something more efficient. If you can't find a way to improve on this, I would suggest templating Entity, as the other answer suggested.
If you don't want to do any major modifications to Entity, you can implement a hidden virtual helper function in World, to actually create the component. In this case, the helper function can take a parameter which indicates what kind of component to construct, and return void*; createComponent() calls the hidden function, specifying ComponentType, and casts the return value to ComponentType*. The easiest way I can think of is to give each component a static member function, create(), and map type indexes to create() calls.
To allow each component to take different parameters, we can use a helper type, let's call it Arguments. This type provides a simple interface while wrapping the actual parameter list, allowing us to easily define our create() functions.
// Argument helper type. Converts arguments into a single, non-template type for passing.
class Arguments {
public:
struct ArgTupleBase
{
};
template<typename... Ts>
struct ArgTuple : public ArgTupleBase {
std::tuple<Ts...> args;
ArgTuple(Ts... ts) : args(std::make_tuple(ts...))
{
}
// -----
const std::tuple<Ts...>& get() const
{
return args;
}
};
// -----
template<typename... Ts>
Arguments(Ts... ts) : args(new ArgTuple<Ts...>(ts...)), valid(sizeof...(ts) != 0)
{
}
// -----
// Indicates whether it holds any valid arguments.
explicit operator bool() const
{
return valid;
}
// -----
const std::unique_ptr<ArgTupleBase>& get() const
{
return args;
}
private:
std::unique_ptr<ArgTupleBase> args;
bool valid;
};
Next, we define our components to have a create() function, which takes a const Arguments& and grabs arguments out of it, by calling get(), dereferencing the pointer, casting the pointed-to ArgTuple<Ts...> to match the component's constructor parameter list, and finally obtaining the actual argument tuple with get().
Note that this will fail if the Arguments was constructed with an improper argument list (one that doesn't match the component's constructor's parameter list), just as calling the constructor directly with an improper argument list would; it will accept an empty argument list, however, due to Arguments::operator bool(), allowing default parameters to be provided. [Unfortunately, at the moment, this code has issues with type conversion, specifically when the types aren't the same size. I'm not yet sure how to fix this.]
// Two example components.
class One {
int i;
bool b;
public:
One(int i, bool b) : i(i), b(b) {}
static void* create(const Arguments& arg_holder)
{
// Insert parameter types here.
auto& args
= static_cast<Arguments::ArgTuple<int, bool>&>(*(arg_holder.get())).get();
if (arg_holder)
{
return new One(std::get<0>(args), std::get<1>(args));
}
else
{
// Insert default parameters (if any) here.
return new One(0, false);
}
}
// Testing function.
friend std::ostream& operator<<(std::ostream& os, const One& one)
{
return os << "One, with "
<< one.i
<< " and "
<< std::boolalpha << one.b << std::noboolalpha
<< ".\n";
}
};
std::ostream& operator<<(std::ostream& os, const One& one);
class Two {
char c;
double d;
public:
Two(char c, double d) : c(c), d(d) {}
static void* create(const Arguments& arg_holder)
{
// Insert parameter types here.
auto& args
= static_cast<Arguments::ArgTuple<char, double>&>(*(arg_holder.get())).get();
if (arg_holder)
{
return new Two(std::get<0>(args), std::get<1>(args));
}
else
{
// Insert default parameters (if any) here.
return new Two('\0', 0.0);
}
}
// Testing function.
friend std::ostream& operator<<(std::ostream& os, const Two& two)
{
return os << "Two, with "
<< (two.c == '\0' ? "null" : std::string{ 1, two.c })
<< " and "
<< two.d
<< ".\n";
}
};
std::ostream& operator<<(std::ostream& os, const Two& two);
Then, with all that in place, we can finally implement Entity, World, and WorldImpl.
// This is the world interface.
class World
{
// Actual worker.
virtual void* create_impl(const std::type_index& ctype, const Arguments& arg_holder) = 0;
// Type-to-create() map.
static std::unordered_map<std::type_index, std::function<void*(const Arguments&)>> creators;
public:
// Templated front-end.
template<typename ComponentType>
ComponentType* createComponent(const Arguments& arg_holder)
{
return static_cast<ComponentType*>(create_impl(typeid(ComponentType), arg_holder));
}
// Populate type-to-create() map.
static void populate_creators() {
creators[typeid(One)] = &One::create;
creators[typeid(Two)] = &Two::create;
}
};
std::unordered_map<std::type_index, std::function<void*(const Arguments&)>> World::creators;
// Just putting in a dummy parameter for now, since this simple example doesn't actually use it.
template<typename Allocator = std::allocator<World>>
class WorldImpl : public World
{
void* create_impl(const std::type_index& ctype, const Arguments& arg_holder) override
{
return creators[ctype](arg_holder);
}
};
class Entity
{
World* world;
public:
template<typename ComponentType, typename... Args>
void assign(Args... args)
{
ComponentType* component = world->createComponent<ComponentType>(Arguments(args...));
std::cout << *component;
delete component;
}
Entity() : world(new WorldImpl<>())
{
}
~Entity()
{
if (world) { delete world; }
}
};
int main() {
World::populate_creators();
Entity e;
e.assign<One>();
e.assign<Two>();
e.assign<One>(118, true);
e.assign<Two>('?', 8.69);
e.assign<One>('0', 8); // Fails; calls something like One(1075929415, true).
e.assign<One>((int)'0', 8); // Succeeds.
}
See it in action here.
That said, this has a few issues:
Relies on typeid for create_impl(), losing the benefits of compile-time type deduction. This results in slower execution than if it was templated.
Compounding the issue, type_info has no constexpr constructor, not even for when the typeid parameter is a LiteralType.
I'm not sure how to obtain the actual ArgTuple<Ts...> type from Argument, rather than just casting-and-praying. Any methods of doing so would likely depend on RTTI, and I can't think of a way to use it to map type_indexes or anything similar to different template specialisations.
Due to this, arguments must be implicitly converted or casted at the assign() call site, instead of letting the type system do it automatically. This... is a bit of an issue.
I have a class which has a template:
template<class T = int> class slider;
The class has a void Process(void) method, so, I think it should be callable regarless of the type, return value is void and there are no parameters to it.
As for now I have this code to call process each frame in my application:
//class menu:
typedef boost::variant<std::shared_ptr<slider<int>>,std::shared_ptr<slider<float>>,std::shared_ptr<slider<double>>,std::shared_ptr<slider<char>>> slider_type;
std::map<std::string,slider_type> Sliders;
//buttons ... etc ...
void Process()
{
if(!Sliders.empty())
{
for(auto i = Sliders.begin(); i != Sliders.end(); ++i)
{
switch(i->second.which())
{
case 0://slider<int>
{
boost::get<std::shared_ptr<slider<int>>>(i->second)->Process();
break;
}
case 1://slider<float>
{
boost::get<std::shared_ptr<slider<float>>>(i->second)->Process();
break;
}
//.....
}
}
}
}
Is it possible to execute the functions Process() like in the following example?
for(auto i = Sliders.begin(); i != Sliders.end(); ++i)
{
switch(i->second.which())
{
boost::get<???Any???>(i->second)->Process();
}
}
If yes, how?
What would such a function return? You can't change the type of a function at runtime. And the point of a variant is that it's contents are determined at runtime.
The only thing it could return is a boost::any. Which is really just exchanging one kind of unknown for another (an unknown that's a lot harder to deal with when you don't know what it contains, mind you). But if you want to see such a visitor:
struct convert_to_any : public boost::static_visitor<boost::any>
{
template<typename T> boost::any operator() (const T& t) {return t;}
};
Use apply_visitor on that, and you will get an any back. Though I fail to see how that's helpful.
In any case, if you're using get on a variant, you are almost certainly doing the wrong thing. The correct way to access the elements of a variant is with a visitor, not with get.
In your case, the visitor should be simple:
struct ProcessVisitor : public boost::static_visitor<>
{
template<typename T> void operator() (const T& t) const {t->Process();}
};
Just use apply_visitor on that. If the variant contains a type that can be used with operator-> and the return value of that function can have Process called on it, then it will.
(Untested code!)
struct CallProcess : static_visitor<>
{
template <class T>
void operator()(const T &t) const
{
t->Process();
}
};
for(auto i = Sliders.begin(); i != Sliders.end(); ++i)
{
boost::apply_visitor(CallProcess(), i->second);
}
No, not at all. You have to visit and deal with the case of every type. That is much better done with a visitor than your switch hack.
It's not possible because boost::variant has no way to know that all the types in the variant have anything in common. In fact, since the compiler generates a distinct class for each template specialization used, the address of the Process() function that would need to be used is different for each type in the boost::variant. To get around this you could abandon variant and use virtual functions and polymorphic classes sharing a common base class.
I have a map which represents a configuration. It's a map of std::string and boost::any.
This map is initialized at the start and I'd like the user to be able to override these options on the command line.
What I'd love to do is build the program options from this map using the options_description::add_option() method. However, it takes a template argument po::value<> whereas all I have is boost::any.
So far, I just have the shell of the code. m_Config represents my configuration class, and getTuples() returns a std::map<std::string, Tuple>. TuplePair is a typedef of std::pair<std::string, Tuple> and the Tuple contains the boost::any I am interested in.
po::options_description desc;
std::for_each(m_Config.getTuples().begin(),
m_Config.getTuples().end(),
[&desc](const TuplePair& _pair)
{
// what goes here? :)
// desc.add_options() ( _pair.first, po::value<???>, "");
});
Is there a way to build it this way, or do I need to resort to doing it myself?
Thanks in advance!
boost::any is not applicable to your problem. It performs the most basic form of type erasure: storage and (type-safe) retrieval, and that's it. As you've seen, no other operations can be performed. As jhasse points out, you could just test every type you want to support, but this is a maintenance nightmare.
Better would be to expand upon the idea boost::any uses. Unfortunately this requires a bit of boiler-plate code. If you'd like to try it, there's a new Boost library being discussed right now on the mailing list (titled "[boost] RFC: type erasure") that is essentially a generalized type erasure utility: you define the operations you'd like your erased type to support, and it generates the proper utility type. (It can simulate boost::any, for example, by requiring the erased type be copy-constructible and type-safe, and can simulate boost::function<> by additionally requiring the type be callable.)
Aside from that, though, your best option is probably to write such a type yourself. I'll do it for you:
#include <boost/program_options.hpp>
#include <typeinfo>
#include <stdexcept>
namespace po = boost::program_options;
class any_option
{
public:
any_option() :
mContent(0) // no content
{}
template <typename T>
any_option(const T& value) :
mContent(new holder<T>(value))
{
// above is where the erasure happens,
// holder<T> inherits from our non-template
// base class, which will make virtual calls
// to the actual implementation; see below
}
any_option(const any_option& other) :
mContent(other.empty() ? 0 : other.mContent->clone())
{
// note we need an explicit clone method to copy,
// since with an erased type it's impossible
}
any_option& operator=(any_option other)
{
// copy-and-swap idiom is short and sweet
swap(*this, other);
return *this;
}
~any_option()
{
// delete our content when we're done
delete mContent;
}
bool empty() const
{
return !mContent;
}
friend void swap(any_option& first, any_option& second)
{
std::swap(first.mContent, second.mContent);
}
// now we define the interface we'd like to support through erasure:
// getting the data out if we know the type will be useful,
// just like boost::any. (defined as friend free-function)
template <typename T>
friend T* any_option_cast(any_option*);
// and the ability to query the type
const std::type_info& type() const
{
return mContent->type(); // call actual function
}
// we also want to be able to call options_description::add_option(),
// so we add a function that will do so (through a virtual call)
void add_option(po::options_description desc, const char* name)
{
mContent->add_option(desc, name); // call actual function
}
private:
// done with the interface, now we define the non-template base class,
// which has virtual functions where we need type-erased functionality
class placeholder
{
public:
virtual ~placeholder()
{
// allow deletion through base with virtual destructor
}
// the interface needed to support any_option operations:
// need to be able to clone the stored value
virtual placeholder* clone() const = 0;
// need to be able to test the stored type, for safe casts
virtual const std::type_info& type() const = 0;
// and need to be able to perform add_option with type info
virtual void add_option(po::options_description desc,
const char* name) = 0;
};
// and the template derived class, which will support the interface
template <typename T>
class holder : public placeholder
{
public:
holder(const T& value) :
mValue(value)
{}
// implement the required interface:
placeholder* clone() const
{
return new holder<T>(mValue);
}
const std::type_info& type() const
{
return typeid(mValue);
}
void add_option(po::options_description desc, const char* name)
{
desc.add_options()(name, po::value<T>(), "");
}
// finally, we have a direct value accessor
T& value()
{
return mValue;
}
private:
T mValue;
// noncopyable, use cloning interface
holder(const holder&);
holder& operator=(const holder&);
};
// finally, we store a pointer to the base class
placeholder* mContent;
};
class bad_any_option_cast :
public std::bad_cast
{
public:
const char* what() const throw()
{
return "bad_any_option_cast: failed conversion";
}
};
template <typename T>
T* any_option_cast(any_option* anyOption)
{
typedef any_option::holder<T> holder;
return anyOption.type() == typeid(T) ?
&static_cast<holder*>(anyOption.mContent)->value() : 0;
}
template <typename T>
const T* any_option_cast(const any_option* anyOption)
{
// none of the operations in non-const any_option_cast
// are mutating, so this is safe and simple (constness
// is restored to the return value automatically)
return any_option_cast<T>(const_cast<any_option*>(anyOption));
}
template <typename T>
T& any_option_cast(any_option& anyOption)
{
T* result = any_option_cast(&anyOption);
if (!result)
throw bad_any_option_cast();
return *result;
}
template <typename T>
const T& any_option_cast(const any_option& anyOption)
{
return any_option_cast<T>(const_cast<any_option&>(anyOption));
}
// NOTE: My casting operator has slightly different use than
// that of boost::any. Namely, it automatically returns a reference
// to the stored value, so you don't need to (and cannot) specify it.
// If you liked the old way, feel free to peek into their source.
#include <boost/foreach.hpp>
#include <map>
int main()
{
// (it's a good exercise to step through this with
// a debugger to see how it all comes together)
typedef std::map<std::string, any_option> map_type;
typedef map_type::value_type pair_type;
map_type m;
m.insert(std::make_pair("int", any_option(5)));
m.insert(std::make_pair("double", any_option(3.14)));
po::options_description desc;
BOOST_FOREACH(pair_type& pair, m)
{
pair.second.add_option(desc, pair.first.c_str());
}
// etc.
}
Let me know if something is unclear. :)
template<class T>
bool any_is(const boost::any& a)
{
try
{
boost::any_cast<const T&>(a);
return true;
}
catch(boost::bad_any_cast&)
{
return false;
}
}
// ...
po::options_description desc;
std::for_each(m_Config.getTuples().begin(),
m_Config.getTuples().end(),
[&desc](const TuplePair& _pair)
{
if(any_is<int>(_pair.first))
{
desc.add_options() { _pair.first, po::value<int>, ""};
}
else if(any_is<std::string>(_pair.first))
{
desc.add_options() { _pair.first, po::value<std::string>, ""};
}
else
{
// ...
}
});
// ...
If you have more than a handful of types consider using typelists.
Suppose I have an autolocker class which looks something like this:
template <T>
class autolocker {
public:
autolocker(T *l) : lock(l) {
lock->lock();
}
~autolocker() {
lock->unlock();
}
private:
autolocker(const autolocker&);
autolocker& operator=(const autolocker&);
private:
T *lock;
};
Obviously the goal is to be able to use this autolocker with anything that has a lock/unlock method without resorting to virtual functions.
Currently, it's simple enough to use like this:
autolocker<some_lock_t> lock(&my_lock); // my_lock is of type "some_lock_t"
but it is illegal to do:
autolocker lock(&my_lock); // this would be ideal
Is there anyway to get template type deduction to play nice with this (keep in my autolocker is non-copyable). Or is it just easiest to just specify the type?
Yes you can use the scope-guard technique
struct autolocker_base {
autolocker_base() { }
protected:
// ensure users can't copy-as it
autolocker_base(autolocker_base const&)
{ }
autolocker_base &operator=(autolocker_base const&)
{ return *this; }
};
template <T>
class autolocker : public autolocker_base {
public:
autolocker(T *l) : lock(l) {
lock->lock();
}
autolocker(const autolocker& o)
:autolocker_base(o), lock(o.lock)
{ o.lock = 0; }
~autolocker() {
if(lock)
lock->unlock();
}
private:
autolocker& operator=(const autolocker&);
private:
mutable T *lock;
};
Then write a function creating the autolocker
template<typename T>
autolocker<T> makelocker(T *l) {
return autolocker<T>(l);
}
typedef autolocker_base const& autolocker_t;
You can then write it like this:
autolocker_t lock = makelocker(&my_lock);
Once the const reference goes out of scope, the destructor is called. It doesn't need to be virtual. At least GCC optimizes this quite well.
Sadly, this means you have to make your locker-object copyable since you need to return it from the maker function. But the old object won't try to unlock twice, because its pointer is set to 0 when it's copied, so it's safe.
Obviously you can't get away with autolocker being a template, because you want to use it as a type, and templates must be instantiated in order to obtain types.
But type-erasure might be used to do what you want. You turn the class template into a class and its constructor into a member template. But then you'd have to dynamically allocate an inner implementation object.
Better, store a pointer to a function that performs the unlock and let that function be an instance of a template chosen by the templatized constructor. Something along these lines:
// Comeau compiles this, but I haven't tested it.
class autolocker {
public:
template< typename T >
autolocker(T *l) : lock_(l), unlock_(&unlock<T>) { l->lock(); }
~autolocker() { unlock_(lock_); }
private:
autolocker(const autolocker&);
autolocker& operator=(const autolocker&);
private:
typedef void (*unlocker_func_)(void*);
void *lock_;
unlocker_func_ unlock_;
template <typename T>
static void unlock(void* lock) { ((T*)lock)->unlock(); }
};
I haven't actually tried this and the syntax might be wrong (I'm not sure how to take the address of a specific function template instance), but I think this should be doable in principle. Maybe someone comes along and fixes what I got wrong.
I like this a lot more than the scope guard, which, for some reason, I never really liked at all.
I think jwismar is correct and what you want is not possible with C++. However, a similar (not direct analogue) construct is possible with C++0x, using several new features (rvalues/moving and auto variable type):
#include <iostream>
template <typename T>
class autolocker_impl
{
public:
autolocker_impl(T *l) : lock(l) {
lock->lock();
}
autolocker_impl (autolocker_impl&& that)
: lock (that.lock)
{
that.lock = 0;
}
~autolocker_impl() {
if (lock)
lock->unlock();
}
private:
autolocker_impl(const autolocker_impl&);
autolocker_impl& operator=(const autolocker_impl&);
private:
T *lock;
};
template <typename T>
autolocker_impl <T>
autolocker (T* lock)
{
return autolocker_impl <T> (lock);
}
struct lock_type
{
void lock ()
{ std::cout << "locked\n"; }
void unlock ()
{ std::cout << "unlocked\n"; }
};
int
main ()
{
lock_type l;
auto x = autolocker (&l);
}
autolocker is a class template, not a class. Your "this would be ideal" is showing something that doesn't make sense in C++.