How to define static member variable from class template with specialization? - c++

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.

Related

implementing a generic binary function with a class and functor as template parameters

I am trying to wrap some templated functions into some binary functors like below. When I try to compile the code I have the error
error: no match for call to ‘(QtyAsc) (myobj&, myobj&)
I thought that being operator() in QtyAsc a function in a class, the template deduction mechanism would have worked but it seems that the compiler doesn't accept myobj classes as valid types for it.
Is it maybe because of the call to boost::bind? I was trying to provide a default implementation for the second templated argument (unfortunately I cannot use C++11 with default templated arguments).
class myobj {
public:
myobj(int val) : qty_(val) {}
int qty() { return qty_;}
private:
int qty_;
};
template<class T>
int get_quantity(const T& o) {
throw runtime_error("get_quantity<T> not implemented");
}
template<>
int get_quantity(const myobj& o) {
return o.qty();
}
struct QtyAsc {
template<class T, class QEx >
bool operator()(const T& o1, const T& o2, QEx extr = boost::bind(&get_quantity<T>,_1)) const {
if(extr(o1) < extr(o2))
return true;
return false;
}
};
int main() {
myobj t1(10),t2(20);
QtyAsc asc;
if(asc(t1,t2))
cout << "Yes" << endl;
}
If you can't use C++11, just provide an additional overload:
struct QtyAsc {
template<class T, class QEx >
bool operator()(const T& o1, const T& o2, QEx extr) const {
return extr(o1) < extr(o2);
}
template<class T>
bool operator()(const T& o1, const T& o2) const {
return operator()(o1, o2, &get_quantity<T>);
}
};
(I've omitted the unnecessary boost::bind.) Also, you will need to declare myobj::qty to be const:
int qty() const {
return qty_;
}
since you want to invoke it on const objects. (Live demo)

How to implement generic accessors for property class

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;
}

use of class template requires template argument list

I moved out the methods implementation from my class and caught the following error:
use of class template requires template argument list
for method whitch doesn't require template type at all... (for other methods all ok)
Class
template<class T>
class MutableQueue
{
public:
bool empty() const;
const T& front() const;
void push(const T& element);
T pop();
private:
queue<T> queue;
mutable boost::mutex mutex;
boost::condition condition;
};
Wrong implementation
template<> //template<class T> also incorrect
bool MutableQueue::empty() const
{
scoped_lock lock(mutex);
return queue.empty();
}
It should be:
template<class T>
bool MutableQueue<T>::empty() const
{
scoped_lock lock(mutex);
return queue.empty();
}
And if your code is that short, just inline it, as you can't separate the implementation and header of a template class anyway.
Use:
template<class T>
bool MutableQueue<T>::empty() const
{
...
}

C++ template class overload [] operator

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;
}

Using STL algorithms (specifically std::sort) from within a templated class

I've declared a template class MyContainer as bellow, then created an instance of it of type DataType1. The DataType1 class provides a friend function "DataSpecificComparison" which is used by std::sort to compare DataType1 objects. The program compiled and sorted correctly.
I then defined a class called DataType2, gave it a friend implementation of "DataSpecificComparison" and used it to create another instance of MyContainer.
I am now unable to compile the program as a "C2914: 'std::sort' : cannot deduce template argument as function argument is ambiguous" compile time error is reported.
How can a developer specify that the DataSpecificComparison binary predicate is to take arguments of template type T*? Or is there another way around this issue?
template <class T>
class MyContainer
{
private:
vector<T*> m_vMyContainerObjects;
....
public:
....
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison)
}
}
class DataType1
{
....
friend bool DataSpecificComparison(const DataType1 * lhs, const DataType1 * rhs)
}
class DataType2
{
....
friend bool DataSpecificComparison(const DataType2* lhs, const DataType2* rhs)
}
You can use a temporary local function pointer variable of the required type to select the correct overload of DataSpecificComparison:
void SortMyContainerObjects()
{
typedef bool (*comparer_t)(const T*, const T*);
comparer_t cmp = &DataSpecificComparison;
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), cmp);
}
Here the compiler can deduce that you want to use the DataSpecificComparison overload that matches the comparer_t type, which resolves the ambiguity.
sth already gave a correct answer, but there's also a direct alternative based on the same principle:
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(),
static_cast<bool (*comparer_t)(const T*, const T*)>(&DataSpecificComparison));
}
This uses essentially the same mechanism. The cast forces overload resolution to happen before the Template Argument Deduction for std::sort.
template<typename T>
struct DataSpecificComp : public binary_function<T, T, bool>
{
public:
bool operator()(const T* lhs, const T* rhs)
{
return *lhs < *rhs;
}
};
call the sort function as shown below:
sort(vi.begin(), vi.end(), DataSpecificComp<int>());
I'd prefer something along the following lines: by default it compares objects with less_than (so you wouldn't have to remember to provide a function with a funny name), and there's an overload that allows giving your own comparison functor (again, value-based):
#include <vector>
#include <algorithm>
#include <functional>
template <class T, class Func>
struct indirect_binary_call_type: public std::binary_function<const T*, const T*, bool>
{
Func f;
indirect_binary_call_type(Func f): f(f) {}
bool operator()(const T* a, const T* b) const
{
return f(*a, *b);
}
};
template <class T, class Func>
indirect_binary_call_type<T, Func> indirect_binary_call(Func f)
{
return indirect_binary_call_type<T, Func>(f);
}
template <class T>
class MyContainer
{
private:
std::vector<T*> m_vMyContainerObjects;
public:
void Sort()
{
Sort(std::less<T>());
}
template <class Func>
void Sort(Func f )
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), indirect_binary_call<T>(f));
}
};
int main()
{
MyContainer<int> m;
m.Sort();
m.Sort(std::greater<int>());
}
Did you try defining DataSpecificComparison as template with bunch of specializations and giving it the type?
template<T>
bool DataSpecificComparison(const T* t1, const T* t2)
{
// something non compilable here
}
template<> bool DataSpecificComparison<Data1>(const Data1* t1, const Data1* t2)
{
// return *t1 < *t2;
}
....
void SortMyContainerObjects()
{
std::sort(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison<T>)
}
....
Templating DataSpecificComparison should work. You can also specifically call the proper std::sort template, but it's a bit cumbersome:
template <class T>
class MyContainer
{
private:
vector<T*> m_vMyContainerObjects;
typedef bool (*compsT)(T, T);
public:
....
void SortMyContainerObjects()
{
std::sort<std::vector<T*>::iterator, compsT>(m_vMyContainerObjects.begin(), m_vMyContainerObjects.end(), DataSpecificComparison);
}
}