How would generic accessors for a property class and their defaults be implemented?
I roughly think it would look as follows:
template<typename Type,
typename Getter = /* How to implement a default setter? */
typename Setter =
class Property {
Getter get; /* Is this right? How is it called then? */
Setter set;
Property(Type value, Getter getter, Setter setter) ...
};
Getter and Setter should be able to be given as lambdas. Is this the right approach, and how do I continue?
You could agree to some kind of structural interface for getters and setters, and then implement something like this:
template <typename T> struct default_getter
{
T & operator()(T & x) const { return x; }
T const & operator()(T const & x) const { return x; }
};
template <typename T> struct default_setter
{
template <typename U>
void operator()(T & x, U && u) const { x = std::forward<U>(u); }
};
template <typename T,
typename Getter = default_getter<T>,
typename Setter = default_setter<T>>
class Property
{
struct PropertyImpl : Getter, Setter
{
T value;
};
PropertyImpl impl;
public:
template <typename U>
void set(U && u)
{
static_cast<Setter &>(impl)(impl.value, std::forward<U>(u));
}
T & get()
{
static_cast<Getter &>(impl)(impl.value);
}
T const & get() const
{
static_cast<Getter const &>(impl)(impl.value);
}
};
Now you can use it like so:
struct Foo
{
Property<Bar> bar;
};
Foo x;
x.bar.get();
x.bar.set(10);
I would propose a solution using std::function.
template<typename T>
struct Property
{
typedef std::function<T()> GetterFunc;
typedef std::function<void(const T&)> SetterFunc;
Property (T value, GetterFunc getter, SetterFunc setter)
: m_getter(getter)
, m_setter(setter)
{
}
Property(T value)
: m_getter( [value](){ return value; }
, m_setter ( [](const T&) { } ) // I have know clue what behaviour you want here
{
}
T Get() { return m_getter(); }
void Set(const T& value) { m_setter(value); }
private:
GetterFunc m_getter;
SetterFunc m_setter;
}
Related
OK I have this code:
// default implementation just uses a mutex to serialize access
template <typename T, typename=void>
struct kernport {
static const bool atomic = false;
// constructor
kernport(T value=T()) {
set(value);
}
// get/set value
T get() const { std::unique_lock<std::mutex> lock(lock_); return value_; }
void set(const T& value) { std::unique_lock<std::mutex> lock(lock_); value_ = value; }
private:
mutable std::mutex lock_;
T value_;
};
// specialization if std::atomic<T> exists
template <typename T>
struct kernport<T, void_type<decltype(std::atomic<T>())>> {
static const bool atomic = true;
// constructor
kernport(T value=T()) {
set(value);
}
// query port value
T get() const { return value_.load(std::memory_order_acquire); }
void set(const T& value) { value_.store(value, std::memory_order_release); }
private:
std::atomic<T> value_;
};
If I try to check kernport<bool>::atomic, I get the dreaded undefined reference. OK, so I need to define the member outside of the class:
template <typename T> const bool kernport<T>::atomic;
Unfortunately, doing that, I get:
inc/skunk/dataflow/kernel.h:472:47: error: template definition of
non-template ‘const bool sk::kernport::atomic’ template const bool kernport::atomic;
And for the life of me I can find the right syntax. How do I write this?
Maybe
template <typename T1, typename T2>
const bool kernport<T1, T2>::atomic;
template <typename T1>
const bool kernport<T1, void>::atomic;
?
Don't forget the second (defaulted to void) template parameter.
For example, I have a class template:
template <typename T>
class base {
public:
void set(T data) { data_=data; }
private:
T data_;
};
And for a certain type I would like to add a function, but also have functions from the template class.
template <>
class base<int>{
public:
void set(int data) { data_=data; }
int get(){ return data_;} //function specific to int
private:
int data_;
}
How to do that without copying all members from the template class?
With inheritance:
template <typename T> struct extra {};
template <> struct extra<int> {
public:
int get() const;
};
template <typename T>
class base : public extra<T> {
friend class extra<T>;
public:
void set(T data) { data_=data; }
private:
T data_ = 0;
};
int extra<int>::get() const{ return static_cast<const base<int>*>(this)->data_;}
Demo
You can do this by using enable_if from type_traits to enable the get function only when the template parameter is int. One example is shown below.
#include <type_traits>
template <typename T>
class base {
public:
template <typename X=T,
std::enable_if_t< std::is_same<X,typename T>::value
&& std::is_same<X,int>::value, bool> = false>
int get() { return data_; }
void set(T data) { data_=data; }
private:
T data_;
};
I have a function template to echo something from the class, for example:
template<typename T>
void say(const std::vector<T>& ts)
{
for (const auto& t : ts) {
std::cout<<t.getDesc()<<std::endl;
}
}
class Base
{
public
Base();
const std::string& getDesc() const {
return m_desc;
}
protected:
std::string m_desc;
}
All objects inherited from Base can use the template function.
std::vector<Base> v;
Base a;
v.push_back(a)
But when I turn to std::reference_wrapper, the function template does not work. I should write another template to fix it.
template<typename T>
void say(const std::vector<std::reference_wrapper<T>>& ts)
{
for (const auto& t : ts) {
std::cout<<t.get().getDesc()<<std::endl;
}
}
The only difference between them is the std::reference_wrapper::get().
So is there any other way to avoid this annoying code? Or should I write std::reference_wrapper version template for all the function I used?
You don't need to rewrite the whole thing. Just use a level of indirection.
namespace detail {
template<typename T>
constexpr T& get(T &t) { return t; }
template<typename T>
constexpr T const& get(T const &t) { return t; }
template<typename T>
constexpr T& get(std::reference_wrapper<T> rt) { return rt; }
}
template<typename T>
void say(const std::vector<T>& ts)
{
for (const auto& t : ts) {
std::cout<< detail::get(t) <<std::endl;
}
}
The above functions in the detail namespace are reusable of course (though could be better named), and can make all your templates work with std::reference_wrapper.
You can use a helper Unwrap template such as boost::unwrap_reference. Or write your own, copying the code from this other answer:
template< typename T >
struct UnwrapReference;
template< typename T >
struct UnwrapReference { typedef T type; }
template< >
struct UnwrapReference< std::reference_wrapper< T > > { typedef T type; }
Now you can write one version of your function to bind them all (untested):
template<typename T>
void say(const std::vector<T>& ts)
{
for (const UnwrapReference<T>::type &t : ts) {
std::cout<<t.getDesc()<<std::endl;
}
}
I would like to overload the [] operator for a template class in respect with the template parameters. Like so:
template<
typename T,
template<typename> class Property,
template<typename> class Key1,
template<typename> class Key2>
class a_map
{
public:
const Property<T>& operator[](const Key1<T>& k) const
{ return _values[k.index()]; }
const Property<T>& operator[](const Key2<T>& k) const
{ return _values[k.index()]; }
protected:
std::vector<Property<T> > _values;
};
I would use this class like so:
int main()
{
a_map<float, prop, key_a, key_b> pm;
}
Basically I want to be able to access the elements inside the _values vector without having to worry to much about the Key types. All that matters is that they have an index() member.
However I get the following error
error C2535: 'const Property &a_map::operator
[](const Key1 &) const' : member function already defined or
declared
even though key_a and key_b are two totaly different types class templates.
Am I missing something? Is the compiler afraid that in certain situation Key1<T> and Key2<T> might actually be the same type?
Edit
These are the class templates used in main
template<typename T>
struct prop
{
T weight;
T height;
};
template<typename T>
class key_a
{
public:
int index() { return _i; }
private:
int _i;
};
template<typename T>
class key_b
{
public:
int index() { return 3; } // Always return 3
Edit
I'm using MVC++ 2008 compiler.
Since both of your operator[] are the same except for the argument type, why not template them?
template <typename TT>
const Property<T>& operator[](const TT& k) const
{
return _values[k.index()];
}
You need to declare your index() functions as const because inside the a_map template you are invoking them through const objects
template<typename T> class key_a {
public:
int index() const // <- `const` is necessary
{ return _i; }
private:
int _i;
};
template<typename T> class key_b {
public:
int index() const // <- `const` is necessary
{ return 3; }
};
But otherwise, everything compiles and work fine for me.
Tried it in VS2010 compiler and get the same error as yours. This is obviously a compiler bug in MSVC++ compilers. Handling of template-template arguments is implemented incorrectly.
To work around the problem I was able to use this technique
template<
typename T,
template<typename> class Property,
template<typename> class Key1,
template<typename> class Key2>
class a_map
{
public:
const Property<T>& operator[](const typename Key1<T>::self& k) const
{ return _values[k.index()]; }
const Property<T>& operator[](const typename Key2<T>::self& k) const
{ return _values[k.index()]; }
protected:
std::vector<Property<T> > _values;
};
and define the key templates as
template<typename T> class key_a {
public:
typedef key_a self;
int index() const { return _i; }
private:
int _i;
};
template<typename T> class key_b {
public:
typedef key_b self;
int index() const { return 3; }
};
This is inelegant, but it makes it work correctly with MSVC++ compilers.
Compiles fine like this... note how Prop/K1/K2 should be defined.
#include <vector>
template<
typename T,
template<typename> class Property,
template<typename> class Key1,
template<typename> class Key2>
class a_map
{
public:
const Property<T>& operator[](const Key1<T>& k) const
{ return _values[k.index()]; }
const Property<T>& operator[](const Key2<T>& k) const
{ return _values[k.index()]; }
protected:
std::vector<Property<T> > _values;
};
template <typename T> struct K1 { int index() const { return 0; } };
template <typename T> struct K2 { int index() const { return 0; } };
template <typename T> struct Prop { };
int main()
{
a_map<float, Prop, K1, K2> m;
}
If I want to create a function template, where the template parameter isn't used in the argument list, I can do it thusly:
template<T>
T myFunction()
{
//return some T
}
But the invocation must specify the 'T' to use, as the compiler doesn't know how to work it out.
myFunction<int>();
But, suppose I wanted to do something similar, but for the '[]' operator.
template
T SomeObject::operator [ unsigned int ]
{
//Return some T
}
Is there any way to invoke this operator?
This doesn't appear valid:
SomeObject a;
a<int>[3];
This should work:
class C
{
public:
template <class T>
T operator[](int n)
{
return T();
}
};
void foo()
{
C c;
int x = c.operator[]<int>(0);
}
But it's of no real value because you'd always have to specify the type, and so it looks like a very ugly function call - the point of an operator overload is to look like an operator invocation.
Boost.Program_options uses this neat syntax:
int& i = a["option"].as<int>();
Which is achieved with something like this:
class variable_value
{
public:
variable_value(const boost::any& value) : m_value(value) {}
template<class T>
const T& as() const {
return boost::any_cast<const T&>(m_value);
}
template<class T>
T& as() {
return boost::any_cast<T&>(m_value);
}
private:
boost::any m_value;
};
class variables_map
{
public:
const variable_value& operator[](const std::string& name) const
{
return m_variables[name];
}
variable_value& operator[](const std::string& name)
{
return m_variables[name];
}
private:
std::map<std::string, variable_value> m_variables;
};
You could adapt this idea to suit your own needs.
Like with any operator, the function name is operator#, so:
a.operator[]<int>(3);
You can use a.operator[]<int>(1);
But why do you want this?
This may not be an optimal solution, but you could directly call the operator as such:
a.operator[](3);
I tried this in g++ with the following test:
class MyClass {
public:
template<class T>
T operator[](unsigned int) {
// do something
return T();
}
};
int main(int argc, char* argv[]) {
MyClass test;
test.operator[]<int>(0);
//test<int>[0]; // doesn't compile, as you mentioned
return 0;
}
If you need to define operator[] then probably define the template at the class level. Something like this:
template<class T>
class C
{
public:
T operator[](int n)
{
return T();
}
};
int main()
{
C<int> c;
int x = c[0];
return 0;
}
I have a hard time coming up with an example where this would be needed (couldn't you just overload the operator instead?), but here's my thoughts anyway:
Since you cannot use the infix operator syntax with templatized operators, you might want to do the template instantiation before you call the operator. A proxy might be a way to do this.
class some_class {
private:
template<class T> class proxy {
some_class* that_;
public:
proxy(some_class* that) : that_(that) {}
T& operator[](std::size_type idx) {return that->get<T>(idx);}
};
template<class T> class const_proxy {
some_class* that_;
public:
proxy(const some_class* that) : that_(that) {}
const T& operator[](std::size_type idx) const {return that->get<T>(idx);}
};
template< typename T > proxy<T> get_array() {return proxy<T>(this);}
template< typename T > const_proxy<T> get_array() const {return proxy<T>(this);}
template< typename T > T& get(std::size_t idx) {/* whatever */}
template< typename T > const T& get(std::size_t idx) const {/* whatever */}
};
// This is a lousy use case.
// Did I already say I have a hard time imagining how to use this?
template< typename T >
void f(some_class& some_object, sid::size_t idx)
{
T& = some_object.get_array<T>()[idx];
}