boost::operators mixed arithmetic - c++

based on the example here http://www.boost.org/doc/libs/release/libs/utility/operators.htm#example, I have implemented the following derived class of boost::numeric::ublas::vector:
namespace Chebyshev
{
template<typename T>
class function_data : public boost::numeric::ublas::vector<T>,
boost::addable<function_data<T> >,
boost::subtractable<function_data<T> >,
boost::multipliable2<function_data<T>, T>,
boost::dividable2<function_data<T>, T>
{
public:
char dataflag;
function_data() : boost::numeric::ublas::vector<T>() {dataflag=0;} ///< The default empty constructor
function_data(const boost::numeric::ublas::vector<T>& vec) : boost::numeric::ublas::vector<T>(vec) {dataflag=0;} ///< The copy constructor without a flag.
function_data(const boost::numeric::ublas::vector<T>& vec, char flag) : boost::numeric::ublas::vector<T>(vec), dataflag(flag) {} ///< The copy constructor with a flag.
~function_data() {} ///< The destructor.
function_data<T>& operator= (const boost::numeric::ublas::vector<T>& in) {boost::numeric::ublas::vector<T>::operator=(in); return *this;} ///< The assignment operator from a boost::numeric::ublas::vector<T>.
function_data<T>& operator= (const function_data<T>& in) {boost::numeric::ublas::vector<T>::operator=(in); dataflag=in.dataflag; return *this;} ///< The assignment operator.
function_data<T>& operator+= (const function_data<T>& in) {this->boost::numeric::ublas::vector<T>::operator+=(in); this->dataflag=this->dataflag; return *this;}
function_data<T>& operator-= (const function_data<T>& in) {this->boost::numeric::ublas::vector<T>::operator-=(in); this->dataflag=this->dataflag; return *this;}
function_data<T>& operator*= (T in) {this->boost::numeric::ublas::vector<T>::operator*=(in); this->dataflag=this->dataflag; return *this;}
function_data<T>& operator/= (T in) {this->boost::numeric::ublas::vector<T>::operator/=(in); this->dataflag=this->dataflag; return *this;}
friend std::ostream& operator<< (std::ostream& os, const function_data<T>& fd) {os << "[type " << fd.dataflag << "] " << static_cast<boost::numeric::ublas::vector<T> >(fd); return os;} ///< The << operator.
};
}
However, compiling the following snippet of code
int main( int argc, char ** argv)
{
Chebyshev::function_data<std::complex<double> > u;
/* some stuff putting values in u */
std::cout << u*2 << std::endl;
return 0;
}
gives a "ISO C++ says that these are ambiguous, even though the worst conversion for the first is better than the worst conversion for the second" warning and proceeds to give the ublas vector_expression version (with u cast as some kind of vector_expression) and my version (with 2 cast as a const std::complex<double>&).
I would like to be able to use mixed arithmetic in my class as in the above snippet of code, but the explanation on the boost::operators website isn't clear to me. What do I have to add or change in my class to allow this?
Also, in the example, the inheritance list has each class inside the last > of the previous class. I don't see any difference in the output of the compiler whether I write it that way or the way I have above. Which is the proper way to write it?
Best regards, Brett.

Boost uBLAS has all the operators that you need to vector arithmetic. Here is a sample program to highlight it:
#include <iostream>
#include "boost/numeric/ublas/vector.hpp"
#include "boost/numeric/ublas/io.hpp"
using namespace boost::numeric::ublas;
int main()
{
vector<int> v1(4);
vector<int> v2(4);
vector<int> v3(4);
vector< std::complex<double> > v4(4);
for (size_t i = 0; i < v1.size(); i++)
v1(i) = (i + 1) * 3;
for (size_t i = 0; i < v1.size(); i++)
v2(i) = (i + 1) * 10;
for (size_t i = 0; i < v4.size(); i++)
v4(i) = std::complex<double>(v1(i), v2(i));
std::cout << "v1: " << v1 << std::endl;
std::cout << "v2: " << v2 << std::endl;
std::cout << "v3 = v2 * 3: " << (v3 = v2 * 3) << std::endl;
std::cout << "v4: " << v4 << std::endl;
std::cout << "v1 + v2: " << v1 + v2 << std::endl;
std::cout << "v1 - v2: " << v1 - v2 << std::endl;
std::cout << "v1 * 3: " << v1 * 3 << std::endl;
std::cout << "(v2 * 2) / 3: " << (v2 * 2) / 3 << std::endl;
std::cout << "v4 * 3: " << v4 * 3 << std::endl;
std::cout << "v4 + v4" << v4 + v4 << std::endl;
std::cout << "element_prod(v1, v2)" << element_prod(v1, v2) << std::endl;
std::cout << "element_div(v2, v1)" << element_div(v2, v1) << std::endl;
return 0;
}
Here is the output I got when I compiled and ran using g++ v4.1.2:
[ublas]$ g++ -o vector vector.cpp
[ublas]$ ./vector
v1: [4](3,6,9,12)
v2: [4](10,20,30,40)
v3 = v2 * 3: [4](30,60,90,120)
v4: [4]((3,10),(6,20),(9,30),(12,40))
v1 + v2: [4](13,26,39,52)
v1 - v2: [4](-7,-14,-21,-28)
v1 * 3: [4](9,18,27,36)
(v2 * 2) / 3: [4](6,13,20,26)
v4 * 3: [4]((9,30),(18,60),(27,90),(36,120))
v4 + v4[4]((6,20),(12,40),(18,60),(24,80))
element_prod(v1, v2)[4](30,120,270,480)
element_div(v2, v1)[4](3,3,3,3)

Related

overloading operator == for pods

I am working on some low level code with high level interfaces and felt need for comparisons operator for unit testing for plain old data types(like FILETIME struct) but since C++ doesn't even provide memberwise comparisons, so I wrote this:
template <typename Type>
std::enable_if_t<std::is_pod<Type>::value, bool> operator==(const Type& a,
const Type& b) {
return std::memcmp(&a, &b, sizeof(Type)) == 0;
}
So my question is, is this a good way or there are some hidden demons which will give me trouble later down the development cycle but it's kinda working for now.
Is C++14 available? If so, consider PFR library, which makes structures into tuples
This question is a restricted variant of Define generic comparison operator, as noted in the comments. An example of the dangers and effects of padding on the proposed operator== for POD is:
template <typename Type>
std::enable_if_t<std::is_pod<Type>::value, bool> operator==(const Type& a,
const Type& b)
{
return std::memcmp(&a, &b, sizeof(Type)) == 0;
}
struct St {
bool a_bool;
int an_int;
};
union Un {
char buff[sizeof(St)];
St st;
};
std::ostream &operator<<(std::ostream & out, const St& data)
{
return out << '{' << std::boolalpha << data.a_bool << ", " << data.an_int << '}';
}
int main()
{
Un un{{1,2,3,4,5}};
new (&un.st) St;
un.st.a_bool = true;
un.st.an_int = 5;
St x={true, 5};
std::cout << "un.a=" << un.st << '\n';
std::cout << "x=" << x << '\n';
std::cout << (x == un.st) << "\n";
return 0;
}
Both un.st and x contain the same data, but un.st contains some garbage in the padded bytes. The padded garbage makes the propose operator== return false for logically equivalent objects. Here is the output I have got for both gcc (head-9.0.0) and clang (head-8.0.0):
un.a={true, 5}
x={true, 5}
false
Update: this happens also with regular new/delete, as run on wandbox.org:
std::enable_if_t<std::is_pod<Type>::value, bool> operator==(const Type& a,
const Type& b)
{
return std::memcmp(&a, &b, sizeof(Type)) == 0;
}
struct St {
bool a_bool;
int an_int;
};
std::ostream &operator<<(std::ostream & out, const St& data)
{
return out << '{' << std::boolalpha << data.a_bool << ", " << data.an_int << '}';
}
static constexpr unsigned N_ELEMENTS = 2;
int main()
{
{
volatile char * arr = new char[sizeof(St) * N_ELEMENTS];
for (unsigned i=0; i < sizeof(St) * N_ELEMENTS ; ++i)
arr[i] = i + 1;
std::cout << "arr = " << (void*)arr << "\n";
delete[] arr;
}
St * ptr_st = new St[N_ELEMENTS];
std::cout << "ptr_st = " << ptr_st << "\n";
for (unsigned i=0 ; i != N_ELEMENTS; ++i) {
ptr_st[i].a_bool = true;
ptr_st[i].an_int = 5;
}
St x={true, 5};
std::cout << "x=" << x << '\n';
std::cout << "ptr_st[1]=" << ptr_st[1] << '\n';
std::cout << (x == ptr_st[1]) << "\n";
return 0;
}
For which the output is:
arr = 0x196dda0
ptr_st = 0x196dda0
x={true, 5}
ptr_st[1]={true, 5}
false

Why is the destructor of this custom allocator being called twice in GCC/MSVS's stdlib

I've been trying to write a simple allocator, I've written a minimal one that just logs its calls.
When trying to use it in some simple std::vector operations - at least on GCC and Visual Studio - the logged behaviour looks intuitive except that the destructor appears to be called before all of the allocations are requested, as well as at the end. On clang everything works as one would expect, so I'm not sure if this is just a compiler issue.
Assuming it's not a compiler error, what's missing from this allocator; or is my understanding of how the allocator is being called wrong and it's just fine?
I have the code below as a live demo here
#include <ios>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
using std::size_t;
struct Indexer
{
static size_t nextId, objectsAlive;
};
size_t Indexer::nextId, Indexer::objectsAlive;
template<typename T>
class DebugAllocator : protected Indexer
{
static std::string formatPointer(const void* p)
{
std::ostringstream s;
s << "[93m0x" << std::hex << std::uppercase << uintptr_t(p) << "[0m";
return s.str();
}
static std::string formatFunctionName(const char* functionName)
{
return "[96m" + std::string(functionName) + "[0m";
}
static std::string indentation()
{
return std::string((objectsAlive + 1) * 4, ' ');
}
public:
using value_type = T;
using pointer = value_type*;
using size_type = std::make_unsigned_t<typename std::pointer_traits<pointer>::difference_type>;
size_t id;
DebugAllocator() noexcept
: id(nextId++)
{
std::cerr << indentation() << "DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ")\n";
++objectsAlive;
}
template<typename T_rhs>
DebugAllocator(const DebugAllocator<T_rhs>& rhs) noexcept
: id(nextId++)
{
std::cerr << indentation() << "DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ", " << rhs.id << ")\n";
++objectsAlive;
}
template<typename T_rhs>
DebugAllocator& operator=(const DebugAllocator<T_rhs>& rhs) noexcept
{
std::cerr << indentation() << id << " = DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ", " << rhs.id << ")\n";
}
~DebugAllocator() noexcept
{
--objectsAlive;
std::cerr << indentation() << "DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ")\n";
}
pointer allocate(size_type n) const
{
value_type* const p((value_type*) new char[sizeof(value_type) * n]);
std::cerr << indentation() << formatPointer(p) << " = DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ", " << n << ")\n";
return p;
}
void deallocate(pointer p, size_type n) const noexcept
{
std::cerr << indentation() << "DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ", " << formatPointer(p) << ", " << n << ")\n";
delete[] (value_type*) p;
}
bool operator==(const DebugAllocator& rhs) const noexcept
{
std::cerr << indentation() << std::boolalpha << true << " = DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ", " << rhs.id << ")\n";
return true;
}
bool operator!=(const DebugAllocator& rhs) const noexcept
{
std::cerr << indentation() << std::boolalpha << false << " = DebugAllocator::" << formatFunctionName(__func__) << '(' << id << ", " << rhs.id << ")\n";
return false;
}
};
int main()
{
std::vector<int, DebugAllocator<int>> v{3};
v.push_back(1);
v.push_back(2);
v.emplace_back(1);
v.insert(std::begin(v) + 2, 4);
v.erase(std::begin(v) + 3);
std::string separator;
for (int& x : v)
{
std::cerr << separator << std::move(x);
separator = ", ";
}
std::cerr << '\n';
}
GCC / MSVS log:
DebugAllocator::DebugAllocator(0)
0xF86C50 = DebugAllocator::allocate(0, 1)
DebugAllocator::~DebugAllocator(0)
0xF86CA0 = DebugAllocator::allocate(0, 2)
DebugAllocator::deallocate(0, 0xF86C50, 1)
0xF86C50 = DebugAllocator::allocate(0, 4)
DebugAllocator::deallocate(0, 0xF86CA0, 2)
0xF86C20 = DebugAllocator::allocate(0, 8)
DebugAllocator::deallocate(0, 0xF86C50, 4)
3, 1, 4, 1
DebugAllocator::deallocate(0, 0xF86C20, 8)
DebugAllocator::~DebugAllocator(0)
clang log:
DebugAllocator::DebugAllocator(0)
0xD886F0 = DebugAllocator::allocate(0, 1)
0xD88710 = DebugAllocator::allocate(0, 2)
DebugAllocator::deallocate(0, 0xD886F0, 1)
0xD886F0 = DebugAllocator::allocate(0, 4)
DebugAllocator::deallocate(0, 0xD88710, 2)
0xD88730 = DebugAllocator::allocate(0, 8)
DebugAllocator::deallocate(0, 0xD886F0, 4)
3, 1, 4, 1
DebugAllocator::deallocate(0, 0xD88730, 8)
DebugAllocator::~DebugAllocator(0)
Apparently
template<typename T_rhs>
DebugAllocator(const DebugAllocator<T_rhs>& rhs)
does not count as a copy constructor. So a compiler-generated copy-constructor is called that you didn't observe.

Operator Overloading

I'm Confused about a topic regarding operator overloading. See the following code:
#include <iostream>;
class Vectors {
public:
int x, y;
Vectors() {};
Vectors(int a,int b) {
x = a, y = b;
}
Vectors operator+(Vectors aso) {
Vectors brandNew;
std::cout << "ASO X " << aso.x << std::endl;
std::cout << "ASO Y " << aso.y << std::endl;
brandNew.x = brandNew.x + aso.x;
brandNew.y = brandNew.y + aso.y;
return (brandNew);
};
};
int main() {
Vectors v1(2,3);
Vectors v2(4,5);
Vectors v3;
v3 = v1 + v2;
std::cout << "Vector V3 X : " << v3.x << std::endl;
std::cout << "VECTOR V3 Y : " << v3.y << std::endl;
}
When I print aso.x, it y gives me 4 and 5. What I want to do is add both v1 and v2; meaning the x of v1 and v2, and the y of v1 and v2. Then, pass it into a Vectors object and return that object.
How do I accomplish that, given what I have above?
You probably meant
Vectors operator+(const Vectors& aso) {
Vectors brandNew;
std::cout << "ASO X " << aso.x << std::endl;
std::cout << "ASO Y " << aso.y << std::endl;
brandNew.x = x + aso.x;
brandNew.y = y + aso.y;
return (brandNew);
};
or
Vectors operator+(const Vectors& aso) {
Vectors brandNew(x + aso.x, y + aso.y);
std::cout << "ASO X " << aso.x << std::endl;
std::cout << "ASO Y " << aso.y << std::endl;
return (brandNew);
};
As from your comment
But about if there were three.
Chain the operators like
int main() {
Vectors v1(2,3);
Vectors v2(4,5);
Vectors v3(7,11);
Vectors v4;
v4 = v1 + v2 + v3;
std::cout << "Vector V4 X : " << v4.x << std::endl;
std::cout << "Vector V4 X : " << v4.y << std::endl;
}
See Live Demo
You can also use external operator+ :
class Vectors {
public:
int x, y;
Vectors() {};
Vectors(int a,int b) {
x = a, y = b;
}
friend Vectors operator+(const Vectors& v1, const Vectors& v2);
};
Vectors operator+(const Vectors& v1, const Vectors& v2) {
Vectors brandNew;
brandNew.x = v1.x + v2.x;
brandNew.y = v1.y + v2.y;
return (brandNew);
};
int main() {
Vectors v1(2,3);
Vectors v2(4,5);
Vectors v3;
v3 = v1 + v2;
std::cout << "Vector V3 X : " << v3.x << std::endl;
std::cout << "VECTOR V3 Y : " << v3.y << std::endl;
}
You haven't initialized the x and y members of brandNew. You'll get random results.
Vectors operator+(Vectors aso) {
Vectors brandNew;
std::cout << "ASO X " << aso.x << std::endl;
std::cout << "ASO Y " << aso.y << std::endl;
brandNew.x = x + aso.x;
brandNew.y = y + aso.y;
return (brandNew);
};
Your code creates the new Vectors object brandNew and then adds the values of it which are initialised to 0 to the values inside the Vectors object you passed in instead of the values in the current object. Which is why when you add v1 tov2 the result has the same values as those inside v2.
Replace
brandNew.x = brandNew.x + aso.x;
brandNew.y = brandNew.y + aso.y;
with
brandNew.x = x + aso.x;
brandNew.y = y + aso.y;
The correct definition of +operator is the following:
Vectors &operator+(const Vectors& aso)
{
std::cout << "ASO X " << aso.x << std::endl;
std::cout << "ASO Y " << aso.y << std::endl;
x = x + aso.x;
y = y + aso.y;
return (*this);
}
above code does not need temporary variable and also does not do unnecessary copy of parameters as is passed by reference.

C++: Where is my rvalue?

Please consider the following code:
#include <iostream>
template <class T>
class value_wrapper
{
public:
value_wrapper(T& pv) : v(pv) { std::cout<< "CONS.REF:" << pv << " AT:" << (void*)this << std::endl; }
value_wrapper(T&& pv) : v(pv) { std::cout<< "CONS.UNIREF:" << pv << " AT:" << (void*)this << std::endl; }
virtual ~value_wrapper() { std::cout<< "DEST:" << v << " AT:" << (void*)this << std::endl; }
value_wrapper(const value_wrapper& ov) : v(ov.v) { std::cout<< "CONS.COPY.REF:" << v << " AT:" << (void*)this << std::endl; }
value_wrapper(value_wrapper&& ov) : v(ov.v) { std::cout<< "CONS.COPY.UNIREF:" << v << " AT:" << (void*)this << std::endl; }
value_wrapper<T>& operator = (value_wrapper<T>&& ov)
{
std::cout<< "ASSI.UNIREF: OF " << v << " AT:" << (void*)this << " TO:" << ov.v << " AT:" <<(void*)&ov << " ADR.VAL:" << (void*)(&ov.v)<< std::endl;
v = ov.v;
return *this;
}
private:
template <typename V> friend value_wrapper<V> operator - (const value_wrapper<V> v1, const V& v2);
T& v;
};
template<typename T>
value_wrapper<T> operator - (const value_wrapper<T> v1, const T& v2)
{
T f = v1.v - v2;
value_wrapper<T> res(f);
std::cout << "MINUS: RESULT:" << res.v << " AT:" << (void*)&res << " ADR.VAL:" << &res.v << std::endl;
return res;
}
template<typename X>
value_wrapper<X> _ (X& a)
{
return value_wrapper<X>(a);
}
int main()
{
int a = 5;
std::cout << "BEFOR:" << a<< std::endl;
_(a) = _(a) - 1;
std::cout << "AFTER:" << a<< std::endl;
return 0;
}
And their online presence:
(The bad one) http://cpp.sh/7yav
(The good one) http://coliru.stacked-crooked.com/a/ea7363eaba68a336
While the first one outputs:
BEFOR:5
CONS.REF:5 AT:0x761cebd52310
CONS.REF:4 AT:0x761cebd52320
MINUS: RESULT:4 AT:0x761cebd52320 ADR.VAL:0x761cebd522cc
CONS.REF:5 AT:0x761cebd52300
ASSI.UNIREF: OF 5 AT:0x761cebd52300 TO:0 AT:0x761cebd52320 ADR.VAL:0x761cebd522cc
DEST:0 AT:0x761cebd52300
DEST:27644 AT:0x761cebd52320
DEST:0 AT:0x761cebd52310
AFTER:0
and the second one outputs:
g++ -std=c++14 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
BEFOR:5
CONS.REF:5 AT:0x7fffc51a5710
CONS.REF:4 AT:0x7fffc51a5720
MINUS: RESULT:4 AT:0x7fffc51a5720 ADR.VAL:0x7fffc51a56fc
CONS.REF:5 AT:0x7fffc51a5700
ASSI.UNIREF: OF 5 AT:0x7fffc51a5700 TO:4 AT:0x7fffc51a5720 ADR.VAL:0x7fffc51a56fc
DEST:4 AT:0x7fffc51a5700
DEST:4 AT:0x7fffc51a5720
DEST:4 AT:0x7fffc51a5710
AFTER:4
So, here comes the question:
what happened to the rvalue from the moment it had the correct value:
MINUS: RESULT:4 AT:0x761cebd52320 ADR.VAL:0x761cebd522cc
Till the correct value disappeared from the same address:
ASSI.UNIREF: OF 5 AT:0x761cebd52300 TO:0 AT:0x761cebd52320 ADR.VAL:0x761cebd522cc
It seems, that I use the same compiler (g++ (Ubuntu 4.8.4-2ubuntu1~14.04) 4.8.4) as the one from cpp.sh because I get the same results...
Is this a compiler bug, or just my misunderstanding of how a standard rvalue should work?
EDIT:
I Expected the line:
ASSI.UNIREF: OF 5 AT:0x761cebd52300 TO:0 AT:0x761cebd52320 ADR.VAL:0x761cebd522cc
to be:
ASSI.UNIREF: OF 5 AT:0x761cebd52300 TO:4 AT:0x761cebd52320 ADR.VAL:0x761cebd522cc
ie: the value 0 to be 4 just as in the other printout, becaue that has to be the correct value, not 0.
I think you have an undefined behaviour here:
template<typename T>
value_wrapper<T> operator - (const value_wrapper<T> v1, const T& v2)
{
T f = v1.v - v2;
value_wrapper<T> res(f);
return res;
}
See? You are returning a value_wrapper<T> that encapsulates a reference to T, and that reference refers to f. Buf f is a local variable, so you return a dangling reference, and that is UB.

How to use comparison operators on variant with contained types?

I'm using variant a lot in my code and I need to make comparisons with the content in some places to test the content of the variant for its value.
For example:
if(equals<int>(aVariant, 0)){
//Something
} else {
//Something else
}
with this simple template function I've written for this purpose:
template<typename V, typename T>
inline bool equals(V& variant, T value){
return boost::get<T>(&variant) && boost::get<T>(variant) == value;
}
This works well, but the code starts to be difficult to read. I prefer to use comparison operators like that:
if(aVariant == 0){
//Something
} else {
//Something else
}
But I wasn't able to come with a valid implementation of the operator. The problem is that the == operator has already been implemented in the variant to fails at compile-time...
Do someone know a way to implement it anyway ? Or a way to disable this limitation ? Even if I have to implement a version for each possible type contained in the variant, that's not a problem.
Thanks
As commented, I think the cleanest way to solve this conundrum would be to enhance the implementation of boost::variant<> with an operator policy (per operator, really) that allows clients to override behaviour for external uses. (Obviously that is a lot of generic programming work).
I have implemented a workaround. This lets you implement custom operators for variants even when it has those implemented in boost/variant.hpp.
My brainwave was to use BOOST_STRONG_TYPEDEF.
The idea is to break overload resolution (or at least make our custom overloads the preferred resolution) by making our variants of a different actual type (it reminds a bit of a 'desperate' ADL barrier: you cannot un-using visible names from a scope, and you cannot go to a 'demilitarized namespace' (the barrier) since the conflicting declarations reside in the class namespace itself; but you can make them not-apply to your 'decoy' type).
Alas, that won't work very well for operator< and family, because boost strong-typedef actually works hard to preserve (weak) total ordering semantics with the 'base' type. In normal English: strong typedefs define operator< as well (delegating to the base type's implementation).
Not to worry, we can do a CUSTOM_STRONG_TYPEDEF and be on our merry way. Look at the test cases in main for proof of concept (output below).
Due to the interesting interactions described, I picked operator< for this demo, but I suppose there wouldn't be anything in your way to get a custom operator== going for your variant types.
#include <boost/variant.hpp>
#include <boost/lexical_cast.hpp>
#include <string>
#include <iostream>
/////////////////////////////////////////////////////
// copied and reduced from boost/strong_typedef.hpp
#define CUSTOM_STRONG_TYPEDEF(T, D) \
struct D \
/*: boost::totally_ordered1< D */ \
/*, boost::totally_ordered2< D, T */ \
/*> > */ \
{ \
T t; \
explicit D(const T t_) : t(t_) {}; \
D(){}; \
D(const D & t_) : t(t_.t){} \
D & operator=(const D & rhs) { t = rhs.t; return *this;} \
D & operator=(const T & rhs) { t = rhs; return *this;} \
operator const T & () const {return t; } \
operator T & () { return t; } \
/*bool operator==(const D & rhs) const { return t == rhs.t; } */\
/*bool operator<(const D & rhs) const { return t < rhs.t; } */\
};
namespace detail
{
typedef boost::variant<unsigned int, std::string> variant_t;
struct less_visitor : boost::static_visitor<bool>
{
bool operator()(const std::string& a, int b) const
{ return boost::lexical_cast<int>(a) < b; }
bool operator()(int a, const std::string& b) const
{ return a < boost::lexical_cast<int>(b); }
template <typename T>
bool operator()(const T& a, const T& b) const
{ return a < b; }
};
struct variant_less
{
less_visitor _helper;
bool operator()(const variant_t& a, const variant_t& b) const
{ return boost::apply_visitor(_helper, a, b); }
};
}
CUSTOM_STRONG_TYPEDEF(detail::variant_t, custom_vt);
namespace
{
bool operator<(const custom_vt& a, const custom_vt& b)
{ return detail::variant_less()(a, b); }
std::ostream& operator<<(std::ostream& os, const custom_vt& v)
{ return os << (const detail::variant_t&)v; }
}
int main()
{
const detail::variant_t I(43), S("42");
const custom_vt i(I), s(S);
// regression test (compare to boost behaviour)
std::cout << "boost: " << I << " < " << S << ": " << std::boolalpha << (I<S) << "\n";
std::cout << "boost: " << S << " < " << I << ": " << std::boolalpha << (S<I) << "\n";
// FIX1: clumsy syntax (works for boost native variants)
detail::variant_less pred;
std::cout << "clumsy: " << i << " < " << s << ": " << std::boolalpha << pred(i,s) << "\n";
std::cout << "clumsy: " << s << " < " << i << ": " << std::boolalpha << pred(s,i) << "\n";
std::cout << "clumsy: " << I << " < " << S << ": " << std::boolalpha << pred(I,S) << "\n";
std::cout << "clumsy: " << S << " < " << I << ": " << std::boolalpha << pred(S,I) << "\n";
// FIX2: neat syntax (requires a custom type wrapper)
std::cout << "custom: " << i << " < " << s << ": " << std::boolalpha << (i<s) << "\n";
std::cout << "custom: " << s << " < " << i << ": " << std::boolalpha << (s<i) << "\n";
}
Output:
boost: 43 < 42: true
boost: 42 < 43: false
clumsy: 43 < 42: false
clumsy: 42 < 43: true
clumsy: 43 < 42: false
clumsy: 42 < 43: true
custom: 43 < 42: false
custom: 42 < 43: true
Now of course, there might be unfortunate interactions if you want to pass your custom_vt into library API's that use TMP to act on variants. However, due to the painless conversions between the two, you should be able to 'fight your way' out by using detail::variant_t at the appropriate times.
This is the price you have to pay for getting syntactic convenience at the call site.