I'm trying to create a flags bitfield using C++11 enum classes. I'm looking for a way to templatize the operators' return types so they can be used as in code below:
#include <iostream>
enum class Flags {
one = 1,
two = 1 << 1,
three = 1 << 2,
four = 1 << 3
};
#define _CONVERT(_operator) \
static_cast<T>(static_cast<int>(lhs) _operator static_cast<int>(rhs))
template <typename T>
T operator & (const Flags& lhs, const Flags& rhs) {
return _CONVERT(&);
}
template <typename T>
T operator | (const Flags& lhs, const Flags& rhs) {
return _CONVERT(|);
}
#undef _convert
int main()
{
Flags flag = Flags::one | Flags::two | Flags::three;
if (flag & Flags::two)
std::cout << "Flag has two" << std::endl;
if (flag & Flags::four)
std::cout << "Flag has four" << std::endl;
std::cout << static_cast<int>(flag) << std::endl;
}
However, there are several problems:
Flags flag = Flags::one | Flags::two | Flags::three; can't deduce type to be Flags
if (flag & Flags::four) can't deduce type to be bool
I'm new to templates and am kinda lost when it comes to template deduction mechanisms. Also, i tried to create create conversion operator
operator bool(const Flags& flag)
but with no result.
First create a helper template:
template<class E>
struct bool_or_enum{
E e;
explicit operator bool()const{return static_cast<bool>(e); }
operator E() const {return e;}
};
Next, create a trait function and type:
namespace magic_operators {
template<class E>
constexpr std::false_type algebraic_enum(E const volatile&) {return {};}
template<class E>
using use_algebra=decltype( algebraic_enum( std::declval<E const volatile&>() ) );
}
Now magic_operators::use_algebra<E> searches using ADL for algebraic_enum overload returning std::true_type on E. This permits enabling the magic anywhere. MSVC 2015 lacks sufficient C++11 support to use the above; replace with traits class.
The meat: our operators. Stick them into a namespace and bring them in with using namespace:
template<class E, std::enable_if_t<magic_operators::use_algebra<E>{}, int> = 0>
bool_or_enum<E> operator&(E const& lhs, E const& rhs){
using U = std::underlying_type_t<E>;
return { E( static_cast<U>(lhs) | static_cast<U>(rhs) ) };
}
And similar for |.
For ~ and ^ you need a bit mask to remain defined behaviour. Have a traits class enum_mask<E> that defaults to E::bit_mask or somesuch to get it.
template<class E, std::enable_if_t<magic_operators::use_algebra<E>{}, int> = 0>
bool_or_enum<E> operator^(E const& lhs, E const& rhs){
using U = std::underlying_type_t<E>;
return { E( enum_mask<E>{} & (static_cast<U>(lhs) ^ static_cast<U>(rhs) ) ) };
}
template<class E, std::enable_if_t<magic_operators::use_algebra<E>{}, int> = 0>
bool_or_enum<E> operator~(E const& e){
using U = std::underlying_type_t<E>;
return { E( enum_mask<E>{} & (~static_cast<U>(e)) ) };
}
This is tricky due to standards requirements on out of gamut enums.
|= and &= isn't hard, but does need to be coded. = and |= and &= etc that support both assignment chaining and implicit bool requires yet more work. I say do not support it.
Oh and mark everything constexpr and add bool_or_enum<E> overloads to the operators.
The above code is not tested or compiled, but the design works.
The end result is:
enum class Bob { a=2, b=7, bit_mask = 0x00ff };
constexpr std::true_type algebraic_enum( Bob const& ){ return {}; }
using namespace algebraic_ops;
int main(){
Bob x=Bob::a;
x = x | Bob::b;
if( x &~ Bob::b ){
std::cout << "cast to bool bitmasking!\n";
}
}
Or somesuch.
While the #yakk-adam-nevraumont is nice and works, I preferred not to have to redefine anything for my enum classes, but rather have them to automatically get operators once I include my inline header...
So, I've done something like this:
#include <type_traits>
namespace EnumUtils {
template <typename T>
constexpr bool is_class() {
if constexpr (std::is_enum_v<T>) {
return !std::is_convertible_v<T, std::underlying_type_t<T>>;
}
return false;
}
template <class EnumType>
struct WrapperImpl {
EnumType e;
constexpr explicit WrapperImpl(EnumType const& en) : e(en) {}
constexpr explicit WrapperImpl(std::underlying_type_t<EnumType> const& en)
: e(static_cast<EnumType>(en)) {}
constexpr explicit operator bool() const { return static_cast<bool>(e); }
constexpr operator EnumType() const { return e; }
constexpr operator std::underlying_type_t<EnumType>() const {
return std::underlying_type_t<EnumType>(e);
}
};
template <class EnumType>
using Wrapper =
std::conditional_t<is_class<EnumType>(), WrapperImpl<EnumType>, void>;
} // namespace EnumUtils
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped> operator&(
EnumType const& first, EnumType const& second) {
return static_cast<Wrapped>(static_cast<Wrapped>(first) &
static_cast<Wrapped>(second));
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped> operator|(
EnumType const& first, EnumType const& second) {
return static_cast<Wrapped>(static_cast<Wrapped>(first) |
static_cast<Wrapped>(second));
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped> operator^(
EnumType const& first, EnumType const& second) {
return static_cast<Wrapped>(static_cast<Wrapped>(first) ^
static_cast<Wrapped>(second));
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped&> operator|=(
EnumType& first, // NOLINT(runtime/references)
EnumType const& second) {
first = (first | second);
return reinterpret_cast<Wrapped&>(first);
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), Wrapped&> operator&=(
EnumType& first, // NOLINT(runtime/references)
EnumType const& second) {
first = (first & second);
return reinterpret_cast<Wrapped&>(first);
}
template <class EnumType, class Wrapped = EnumUtils::Wrapper<EnumType>>
constexpr std::enable_if_t<EnumUtils::is_class<EnumType>(), EnumType> operator~(
EnumType const& first) {
return static_cast<EnumType>(~static_cast<Wrapped>(first));
}
Related
Why does this code work with the #if 0 block in place, but fails with a fairly complex set of error messages if you remove it? And more importantly, how do I make it the same result as the very similar block above it?
#include <ranges>
#include <iterator>
#include <optional>
#include <string_view>
#include <iostream>
#include <algorithm>
template <::std::ranges::view View,
typename Pred>
requires ::std::ranges::input_range<View> &&
::std::ranges::common_range<View> &&
::std::is_object_v<Pred> &&
::std::indirect_unary_predicate<const Pred, ::std::ranges::iterator_t<View>>
class skip_after_view : public ::std::ranges::view_interface<skip_after_view<View, Pred>>
{
public:
skip_after_view() = default;
skip_after_view(View v, Pred p)
: subview_(::std::move(v)), pred_(::std::move(p))
{}
class iterator;
friend class iterator;
auto begin() const {
return iterator{subview_.begin(), subview_.end(), &pred_};
}
auto end() const {
return iterator{subview_.end(), subview_.end(), &pred_};
}
private:
View subview_ = View();
Pred pred_;
};
template <typename View, typename Pred>
class skip_after_view<View, Pred>::iterator
{
using parent_t = View::iterator;
using parent_traits = ::std::iterator_traits<parent_t>;
friend class skip_after_view<View, Pred>;
public:
using value_type = parent_traits::value_type;
using reference = parent_traits::reference;
using pointer = parent_traits::pointer;
using difference_type = ::std::ptrdiff_t;
using iterator_category = ::std::input_iterator_tag;
constexpr iterator() = default;
auto operator *() { return *me_; }
auto operator *() const { return *me_; }
iterator &operator ++() {
for (bool last_pred = true; last_pred; ) {
if (end_ != me_) {
last_pred = (*pred_)(operator *());
++me_;
} else {
last_pred = false;
}
}
return *this;
}
void operator ++(int) {
++(*this);
}
friend
bool operator ==(iterator const &a, iterator const &b) {
return a.me_ == b.me_;
}
private:
parent_t me_;
parent_t end_;
Pred const *pred_ = nullptr;
iterator(parent_t const &me, parent_t end, Pred const *pred)
: me_(me), end_(::std::move(end)), pred_(pred)
{}
};
template <std::ranges::range Range, typename Pred>
skip_after_view(Range&&) -> skip_after_view<std::ranges::views::all_t<Range>, Pred>;
struct skip_after_adaptor {
template <typename Pred>
class closure {
friend class skip_after_adaptor;
Pred pred;
explicit closure(Pred &&p) : pred(::std::move(p)) {}
public:
template <typename Range>
auto operator ()(Range &&range) {
return skip_after_view(::std::forward<Range>(range),
::std::move(pred));
}
};
template <typename Pred>
auto operator ()(Pred pred) const {
return closure<Pred>(::std::move(pred));
}
template <typename Range, typename Pred>
auto operator()(Range &&range, Pred &&pred) const {
return skip_after_view(::std::forward(range), ::std::forward(pred));
}
template <typename Range, typename Pred>
friend auto operator|(Range&& rng, closure<Pred> &&fun) {
return fun(std::forward<Range>(rng));
}
};
constexpr auto skip_after = skip_after_adaptor{};
template <::std::input_iterator it>
void check(it const &)
{}
int main()
{
using ::std::string_view;
using namespace ::std::ranges::views;
using ::std::ostream_iterator;
using ::std::ranges::copy;
using ::std::cout;
auto after_e = [](char c) { return c == 'e'; };
constexpr string_view sv{"George Orwell"};
int sum = 0;
{
cout << '[';
copy(sv | skip_after(after_e) | take(6),
ostream_iterator<char>(cout));
cout << "]\n";
}
#if 0
{
auto tmp = skip_after(after_e) | take(6);
cout << '[';
copy(sv | tmp, ostream_iterator<char>(cout));
cout << "]\n";
}
#endif
return sum;
}
Obligatory Compiler Explorer link
If what I want isn't cleanly possible, is there an ugly way to do it? For example, could I make my own composition mechanism and have an ugly bunch of garbage to interface it with the existing views.
There's no way to write a range adapter closure object that composes with the standard ones in C++20. The standard library doesn't expose the mechanism it uses for that composition.
sv | skip_after(after_e) | take(6) works because sv | skip_after(after_e) hits your operator|, which produces a view that can then be used with take(6).
There's a good chance that C++23 will expose the composition mechanism.
For some reason, this constexpr is not being evaluated correctly in a template parameter context:
#include <iostream>
#include <functional>
namespace detail
{
// Reason to use an enum class rahter than just an int is so as to ensure
// there will not be any clashes resulting in an ambigious overload.
enum class enabler
{
enabled
};
}
#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), detail::enabler> = detail::enabler::enabled
#define ENABLE_IF_DEFINITION(...) std::enable_if_t<(__VA_ARGS__), detail::enabler>
namespace detail
{
template <typename T, bool IS_BUILTIN>
class is_value
{
T item_to_find;
std::function<bool(T const& lhs, T const& rhs)> predicate;
public:
constexpr is_value(T item_to_find, std::function<bool(T, T)> predicate)
: item_to_find(item_to_find)
, predicate(predicate)
{}
constexpr bool one_of() const
{
return false;
}
template <typename T1, typename...Ts>
constexpr bool one_of(T1 const & item, Ts const&...args) const
{
return predicate(item_to_find, item) ? true : one_of(args...);
}
};
template <typename T>
class is_value<T, false>
{
T const& item_to_find;
std::function<bool(T const& lhs, T const& rhs)> predicate;
public:
constexpr is_value(T const& item_to_find, std::function<bool(T const&, T const&)> predicate)
: item_to_find(item_to_find)
, predicate(predicate)
{}
constexpr bool one_of() const
{
return false;
}
template <typename T1, typename...Ts>
constexpr bool one_of(T1 const & item, Ts const&...args) const
{
return predicate(item_to_find, item) ? true : one_of(args...);
}
};
}
// Find if a value is one of one of the values in the variadic parameter list.
// There is one overload for builtin types and one for classes. This is so
// that you can use builtins for template parameters.
//
// Complexity is O(n).
//
// Usage:
//
// if (is_value(1).one_of(3, 2, 1)) { /* do something */ }
//
template <typename T, ENABLE_IF(!std::is_class<T>::value)>
constexpr auto const is_value(T item_to_find, std::function<bool(T, T)> predicate = [](T lhs, T rhs) { return lhs == rhs; })
{
return detail::is_value<T, true>(item_to_find, predicate);
}
template <typename T, ENABLE_IF(std::is_class<T>::value)>
constexpr auto const is_value(T const& item_to_find, std::function<bool(T const&, T const&)> predicate = [](T const& lhs, T const& rhs) { return lhs == rhs; })
{
return detail::is_value<T, false>(item_to_find, predicate);
}
template <int I, ENABLE_IF(is_value(I).one_of(3,2,1))>
void fn()
{
}
int main()
{
fn<3>();
std::cout << "Hello, world!\n" << is_value(3).one_of(3,2,1);
}
I've tested this with clang, g++ and vc++. Each had different errors:
clang
source_file.cpp:98:5: error: no matching function for call to 'fn'
fn<3>();
^~~~~
source_file.cpp:92:10: note: candidate template ignored: substitution failure [with I = 3]: non-type template argument is not a constant expression
void fn()
^
1 error generated.
g++
source_file.cpp: In function ‘int main()’:
source_file.cpp:98:11: error: no matching function for call to ‘fn()’
fn<3>();
...
vc++
source_file.cpp(91): fatal error C1001: An internal error has occurred in the compiler.
(compiler file 'msc1.cpp', line 1421)
...
Is my code invalid or are the compilers just not up to the job yet?
Your code is invalid. The compiler (GCC7.1 for me) provides helpful error that allows us to solve this problem.
Issue:
...\main.cpp|19|note: 'detail::is_value<int, true>' is not literal because:|
...\main.cpp|19|note: 'detail::is_value<int, true>' has a non-trivial destructor|
The reason detail::is_value does not have a trivial destructor is due to the std::function<> member; std::function<> might perform dynamic memory allocation (among other reasons), thus it is not trivial. You have to replace it with a trivially destructible type; I present a simple solution below.
Note: Even if std::function<> was trivially destructible, its operator() does not seem to be declared as constexpr (see: http://en.cppreference.com/w/cpp/utility/functional/function/operator()), so it would not work either.
Sample working code (adapt as needed):
#include <iostream>
#include <functional>
namespace detail
{
// Reason to use an enum class rahter than just an int is so as to ensure
// there will not be any clashes resulting in an ambigious overload.
enum class enabler
{
enabled
};
}
#define ENABLE_IF(...) std::enable_if_t<(__VA_ARGS__), detail::enabler> = detail::enabler::enabled
#define ENABLE_IF_DEFINITION(...) std::enable_if_t<(__VA_ARGS__), detail::enabler>
namespace detail
{
// notice the new template parameter F
template <typename T, typename F, bool IS_BUILTIN>
class is_value
{
T item_to_find;
F predicate;
public:
constexpr is_value(T item_to_find, F predicate)
: item_to_find(item_to_find)
, predicate(predicate)
{}
constexpr bool one_of() const
{
return false;
}
template <typename T1, typename...Ts>
constexpr bool one_of(T1 const & item, Ts const&...args) const
{
return predicate(item_to_find, item) ? true : one_of(args...);
}
};
template <typename T, typename F>
class is_value<T, F, false>
{
T const& item_to_find;
F predicate;
public:
constexpr is_value(T const& item_to_find, F predicate)
: item_to_find(item_to_find)
, predicate(predicate)
{}
constexpr bool one_of() const
{
return false;
}
template <typename T1, typename...Ts>
constexpr bool one_of(T1 const& item, Ts const&... args) const
{
return predicate(item_to_find, item) ? true : one_of(args...);
}
};
}
// sample predicate
template<class T>
struct default_compare
{
constexpr bool operator()(T const& lhs, T const& rhs) const
noexcept(noexcept(std::declval<T const&>() == std::declval<T const&>()))
{
return lhs == rhs;
}
};
// Find if a value is one of one of the values in the variadic parameter list.
// There is one overload for builtin types and one for classes. This is so
// that you can use builtins for template parameters.
//
// Complexity is O(n).
//
// Usage:
//
// if (is_value(1).one_of(3, 2, 1)) { /* do something */ }
//
template <typename T, typename F = default_compare<T>, ENABLE_IF(!std::is_class<T>::value)>
constexpr auto const is_value(T item_to_find, F predicate = {})
{
return detail::is_value<T, F, true>(item_to_find, predicate);
}
template <typename T, typename F = default_compare<T>, ENABLE_IF(std::is_class<T>::value)>
constexpr auto const is_value(T const& item_to_find, F predicate = {})
{
return detail::is_value<T, F, false>(item_to_find, predicate);
}
template <int I, ENABLE_IF(is_value(I).one_of(3,2,1))>
void fn()
{
}
int main()
{
fn<3>();
std::cout << "Hello, world!\n" << is_value(3).one_of(3,2,1);
}
I need to alias std::get function in order to improve readability in my code.
Unfortunately I got a compile-time error get<0> in namespace ‘std’ does not name a type. using is equivalent to typedef so it needs types to work with.
I am using a std::tuple to represent some data type:
using myFoo = std::tuple<int,int,double,string>;
using getNumber = std::get<0>;
I look at some previous questions but the solution proposed is to wrap and use std::forward. I don't want to write such code for each member.
Q1 from SO
Q2 from SO
Is there a way to get around this using only using keyword?
is there a way to get around this using only using keyword?
I would say no, for std::get is not a type (thus it's not eligible for such an use).
Moreover, even if it was possible, note that std::get is an overloaded function, thus you would have been required to bind yourself to a specific implementation.
That said, in C++17, you can do something like this:
#include<tuple>
#include<utility>
using myFoo = std::tuple<int,int,double>;
constexpr auto getNumber = [](auto &&t) constexpr -> decltype(auto) { return std::get<0>(std::forward<decltype(t)>(t)); };
template<int> struct S {};
int main() {
constexpr myFoo t{0,0,0.};
S<getNumber(t)> s{};
(void)s;
}
As you can see, constexpr lambdas and variables help you creating compile-time (let me say) wrappers you can use to rename functions.
As correctly pointed out by #T.C. in the comments, if you want to generalize it even more and get an almost perfect alias for std::get, you can use a variable template:
template<int N>
constexpr auto getFromPosition = [](auto &&t) constexpr -> decltype(auto) { return std::get<N>(std::forward<decltype(t)>(t)); };
Now you can invoke it as it follows:
S<getFromPosition<0>(t)> s{};
See it on wandbox.
You can do it with a using + an enum:
#include<tuple>
using myFoo = std::tuple<int,int,double>;
int main() {
constexpr myFoo t{0,0,0.};
enum { Number = 0 };
using std::get;
auto&& x = get<Number>(t);
(void)x;
}
Although unfortunately, this is not DRY since you have to maintain an enum and a tuple simultaneously.
In my view, the most DRY and safest way to achieve this is to use tagged values in the tuple. Limit the tuple to maximum one of each tag type.
The tag is essentially a mnemonic for some unique concept:
#include <tuple>
#include <iostream>
//
// simple example of a tagged value class
//
template<class Type, class Tag>
struct tagged
{
constexpr tagged(Type t)
: value_(t) {}
operator Type&() { return value_; }
operator Type const&() const { return value_; }
Type value_;
};
struct age_tag {};
struct weight_tag {};
struct height_tag {};
using Age = tagged<int, age_tag>;
using Weight = tagged<int, weight_tag>;
using Height = tagged<double, height_tag>;
int main()
{
constexpr auto foo1 = std::make_tuple(Age(21), Weight(150), Height(165.5));
constexpr auto foo2 = std::make_tuple(Weight(150), Height(165.5), Age(21));
using std::get;
//
// note below how order now makes no difference
//
std::cout << get<Age>(foo1) << std::endl;
std::cout << get<Weight>(foo1) << std::endl;
std::cout << get<Height>(foo1) << std::endl;
std::cout << "\n";
std::cout << get<Age>(foo2) << std::endl;
std::cout << get<Weight>(foo2) << std::endl;
std::cout << get<Height>(foo2) << std::endl;
}
expected output:
21
150
165.5
21
150
165.5
In general, tuple should be used in generic code.
If you know field 1 is a Number or a Chicken, you shouldn't be using a tuple. You should be using a struct with a field called Number.
If you need tuple-like functionality (as one does), you can simply write as_tie:
struct SomeType {
int Number;
std::string Chicken;
auto as_tie() { return std::tie(Number, Chicken); }
auto as_tie() const { return std::tie(Number, Chicken); }
};
Now you can access SomeType as a tuple of references by typing someInstance.as_tie().
This still doesn't give you < or == etc for free. We can do that in one place and reuse it everywhere you use the as_tie technique:
struct as_tie_ordering {
template<class T>
using enable = std::enable_if_t< std::is_base_of<as_tie_ordering, std::decay_t<T>>, int>;
template<class T, enable<T> =0>
friend bool operator==(T const& lhs, T const& rhs) {
return lhs.as_tie() == rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator!=(T const& lhs, T const& rhs) {
return lhs.as_tie() != rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator<(T const& lhs, T const& rhs) {
return lhs.as_tie() < rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator<=(T const& lhs, T const& rhs) {
return lhs.as_tie() <= rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator>=(T const& lhs, T const& rhs) {
return lhs.as_tie() >= rhs.as_tie();
}
template<class T, enable<T> =0>
friend bool operator>(T const& lhs, T const& rhs) {
return lhs.as_tie() > rhs.as_tie();
}
};
which gives us:
struct SomeType:as_tie_ordering {
int Number;
std::string Chicken;
auto as_tie() { return std::tie(Number, Chicken); }
auto as_tie() const { return std::tie(Number, Chicken); }
};
and now
SomeTime a,b;
bool same = (a==b);
works. Note that as_tie_ordering doesn't use CRTP and is an empty stateless class; this technique uses Koenig lookup to let instances find the operators.
You can also implement an ADL-based get
struct as_tie_get {
template<class T>
using enable = std::enable_if_t< std::is_base_of<as_tie_get, std::decay_t<T>>, int>;
template<std::size_t I, class T,
enable<T> =0
>
friend decltype(auto) get( T&& t ) {
using std::get;
return get<I>( std::forward<T>(t).as_tie() );
}
};
Getting std::tuple_size to work isn't as easy, sadly.
The enable<T> =0 clauses above should be replaced with class=enable<T> in MSVC, as their compiler is not C++11 compliant.
You'll note above I use tuple; but I'm using it generically. I convert my type to a tuple, then use tuple's < to write my <. That glue code deals with tie as a generic bundle of types. That is what tuple is for.
I am trying to find a way to compare boost::variant with underlying value without constructing variant from this underlying value. The question is defined in the comment in "main()" function
And auxiliary question is about the comparison operators defined in the code. How to decrease the # of comparison operators? If boost::variant contains, say, 6 different types, do I have to define 6! operators to be able to compare two variants?
Thanks!
#include <boost/variant.hpp>
namespace test {
namespace Tag {
struct Level1{ int t{ 1 }; };
struct Level2{ int t{ 2 }; };
}
template <typename Kind> struct Node;
using LevelOne = Node<Tag::Level1>;
using LevelTwo = Node<Tag::Level2>;
using VariantNode = boost::variant
<
boost::recursive_wrapper<LevelOne>,
boost::recursive_wrapper<LevelTwo>
>;
typedef VariantNode* pTree;
typedef std::vector<pTree> lstTree;
template <typename Kind> struct Node
{
Node(pTree p, std::string n) : parent(p), name(n) {}
Node(const Node& another) : name(another.name), parent(another.parent) {}
virtual ~Node() {}
std::string name;
pTree parent;
};
bool operator == (const LevelOne& one, const LevelTwo& two) {
return false;
}
bool operator == (const LevelTwo& two, const LevelOne& one) {
return false;
}
bool operator == (const LevelOne& one, const LevelOne& two) {
return true;
}
bool operator == (const LevelTwo& one, const LevelTwo& two) {
return true;
}
}
int main(int argc, char *argv[])
{
using namespace test;
LevelOne l1(nullptr, "level one");
VariantNode tl2 = VariantNode(LevelTwo(nullptr, "level two"));
VariantNode tl1 = VariantNode(LevelOne(nullptr, "level one"));
bool rv = (tl1 == tl2); // this line compiles OK (comparing two variants)
// comparison below does not compile, because "l1" is not a variant.
// Question: How can I compare "variant" value "tl1"
// with one of the possible content values "l1"
bool rv1 = (tl1 == l1);
return 1;
}
The following will work with any number of types in the variant:
template<typename T>
struct equality_visitor : boost::static_visitor<bool> {
explicit constexpr equality_visitor(T const& t) noexcept : t_{ &t } { }
template<typename U, std::enable_if_t<std::is_same<T, U>::value>* = nullptr>
constexpr bool operator ()(U const& u) const {
return *t_ == u;
}
template<typename U, std::enable_if_t<!std::is_same<T, U>::value>* = nullptr>
constexpr bool operator ()(U const&) const {
return false;
}
private:
T const* t_;
};
template<
typename T,
typename... Ts,
typename = std::enable_if_t<
boost::mpl::contains<typename boost::variant<Ts...>::types, T>::value
>
>
bool operator ==(T const& t, boost::variant<Ts...> const& v) {
equality_visitor<T> ev{ t };
return v.apply_visitor(ev);
}
template<
typename T,
typename... Ts,
typename = std::enable_if_t<
boost::mpl::contains<typename boost::variant<Ts...>::types, T>::value
>
>
bool operator !=(T const& t, boost::variant<Ts...> const& v) {
return !(t == v);
}
The catch is that comparisons must always be of the form value == variant or value != variant rather than variant == value or variant != value. This is because boost::variant<> itself defines these operators to always static_assert, and there is no way for us to make a global operator more specialized than variant<>'s built-in ones.
Online Demo
I want to check object of following types if they are almost/close to expected value.
class MyTypeWithDouble
{
public:
MyTypeWithDouble(double);
bool operator == (const MyTypeWithDouble& rhs) const; //Checks for Equality
private:
double m;
};
/////////////////////////////////////////
class MyTypeWithVector
{
public:
MyTypeWithVector(std::vector<double>v);
bool operator == (const MyTypeWithVector& rhs) const; //Checks for Equality
private:
std::vector<double> v;
};
So that the unit test looks like this
/// TEST1 ////////////////
MyTypeWithDouble t1(42.100001);
BOOST_CHECK_CLOSE(t1,42.1,0.1);
//////TEST2//////////////////////////////
std::vector<double> v; //no initalizer do not have c++11 :-(
v.push_back(42.1);
MyTypeWithVector t2(v);
std::vector<double> compare;
compare.push_back(42.100001);
MY_OWN_FUNCTION_USING_BOOST(t2,compare,0.1); //There is only BOOST_CHECK_EQUAL_COLLECTION available for collections
I do not want to change the implementation of of the '== operator's or
provide Getters.
Adding other operators functions is OK.
Thanks, ToBe
I think you're overengineering this. I'd suggest a simple macro, perhaps with a suitable friend definition. That said, let's accept the challenge.
Necessary adjustments
Your type should
be default-constructible for the check_is_close_t implementation.
also, there must be a way to get at the value, and since you refuse to create a getter, the only option left is to declare an accessor class as friend
We get
class MyTypeWithDouble
{
public:
constexpr MyTypeWithDouble(double v = 0) : m(v) {}
MyTypeWithDouble& operator=(MyTypeWithDouble const&) noexcept = default;
constexpr MyTypeWithDouble(MyTypeWithDouble const&) noexcept = default;
private:
friend class unittest_op::access;
double m;
};
With a bit of tedious work (in a header?) we can use this access loophole to implement everything else. How? Well, we define a "getter", alright, but outside the class definition.
I defined a trait class template inside access (so it is implicitly a friend) and you can specialize for your "floating-point-like" type:
namespace unittest_op {
template<> class access::impl<MyTypeWithDouble> {
public:
typedef double result_type;
static result_type call(MyTypeWithDouble const& v) { return v.m; }
};
}
That's All. Well, that's all for you as a type/test implementor. Of course, we still need to make this work.
The Nuts And Bolts
The unittest_op namespace exists for the sole reason to define "relaying" operators that know how to access the contained value inside your custom type.
Note how we
didn't need to add anything to your user-defined type
we get mixed operands as well (e.g. 2 * MyTypeWithDouble(7.0) -> MyTypeWithDouble(14.0))
and we also defined operator<< so the assert macros know how to print values of MyTypeWithDouble
Thanks to c++11 the work is not complicated:
namespace unittest_op {
class access {
template<typename T, typename Enable = void> class impl;
template<typename T>
class impl<T, typename std::enable_if<std::is_arithmetic<T>::value, void>::type>
{
public: typedef T result_type;
static T & call(T& v) { return v; }
static T const& call(T const& v) { return v; }
};
public:
template<typename T>
static typename impl<T>::result_type do_access(T const& v) { return impl<T>::call(v); }
template<typename T> static constexpr bool can_access(decltype(do_access(std::declval<T>()))*) { return true; }
template<typename T> static constexpr bool can_access(...) { return false; }
};
template<typename T>
typename std::enable_if<access::can_access<T>(nullptr) && not std::is_arithmetic<T>::value, std::ostream&>::type
operator<<(std::ostream& os, T const& v) { return os << "{" << access::do_access(v) << "}"; }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator-(T const& lhs) { return - access::do_access(lhs); }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator+(T const& lhs) { return + access::do_access(lhs); }
#define UNITTEST_OP_BINOP(OP) \
template <typename T1, typename T2> \
static decltype(access::do_access(std::declval<T1>()) OP access::do_access(std::declval<T2>())) \
operator OP(T1 const& lhs, T2 const& rhs) { return access::do_access(lhs) OP access::do_access(rhs); } \
using ::unittest_op::operator OP;
UNITTEST_OP_BINOP(==)
UNITTEST_OP_BINOP(!=)
UNITTEST_OP_BINOP(< )
UNITTEST_OP_BINOP(> )
UNITTEST_OP_BINOP(<=)
UNITTEST_OP_BINOP(>=)
UNITTEST_OP_BINOP(+ )
UNITTEST_OP_BINOP(- )
UNITTEST_OP_BINOP(% )
UNITTEST_OP_BINOP(* )
UNITTEST_OP_BINOP(/ )
// assign-ops only for lvalue types (i.e. identity `access::impl<T>`)
UNITTEST_OP_BINOP(+=)
UNITTEST_OP_BINOP(-=)
UNITTEST_OP_BINOP(%=)
UNITTEST_OP_BINOP(*=)
UNITTEST_OP_BINOP(/=)
#undef UNITTEST_OP_BINOP
}
Note that these are all "open" templates, and we've taken the necessary precautions to make sure that these operators only apply iff do_access is defined and the type wasn't an arithmetic type to begin with.
Why These Precautions?
Well. We're going to do a power-move: We're going to inject our operator overloads into the boost::test_tools namespace, so that BOOST_CHECK* macro implementation can find them.
Had we not taken the precautions just mentioned, we would invite a lot of problems due to ambiguous operator overloads for types that we didn't care about.
The Power Grab
The power grab is simple: we inject (using) each of our operator templates inside the boost::test_tools namespace.
Now we're good to go:
Live On Coliru
BOOST_AUTO_TEST_CASE(my_test)
{
MyTypeWithDouble v(4);
BOOST_CHECK_CLOSE(3.99, v, MyTypeWithDouble(0.1));
}
Prints
Running 2 test cases...
main.cpp(117): error in "my_test": difference{0.25%} between 3.99{3.9900000000000002} and v{{4}} exceeds {0.10000000000000001}%
Full Program
Live On Coliru
#include <utility>
#include <type_traits>
#include <iostream>
namespace unittest_op {
class access {
template<typename T, typename Enable = void> class impl;
template<typename T>
class impl<T, typename std::enable_if<std::is_arithmetic<T>::value, void>::type>
{
public: typedef T result_type;
static T & call(T& v) { return v; }
static T const& call(T const& v) { return v; }
};
public:
template<typename T>
static typename impl<T>::result_type do_access(T const& v) { return impl<T>::call(v); }
template<typename T> static constexpr bool can_access(decltype(do_access(std::declval<T>()))*) { return true; }
template<typename T> static constexpr bool can_access(...) { return false; }
};
template<typename T>
typename std::enable_if<access::can_access<T>(nullptr) && not std::is_arithmetic<T>::value, std::ostream&>::type
operator<<(std::ostream& os, T const& v) { return os << "{" << access::do_access(v) << "}"; }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator-(T const& lhs) { return - access::do_access(lhs); }
template <typename T, typename Enable=decltype(access::do_access(std::declval<T>())) >
static T operator+(T const& lhs) { return + access::do_access(lhs); }
#define UNITTEST_OP_BINOP(OP) \
template <typename T1, typename T2> \
static decltype(access::do_access(std::declval<T1>()) OP access::do_access(std::declval<T2>())) \
operator OP(T1 const& lhs, T2 const& rhs) { return access::do_access(lhs) OP access::do_access(rhs); } \
using ::unittest_op::operator OP;
UNITTEST_OP_BINOP(==)
UNITTEST_OP_BINOP(!=)
UNITTEST_OP_BINOP(< )
UNITTEST_OP_BINOP(> )
UNITTEST_OP_BINOP(<=)
UNITTEST_OP_BINOP(>=)
UNITTEST_OP_BINOP(+ )
UNITTEST_OP_BINOP(- )
UNITTEST_OP_BINOP(% )
UNITTEST_OP_BINOP(* )
UNITTEST_OP_BINOP(/ )
// assign-ops only for lvalue types (i.e. identity `access::impl<T>`)
UNITTEST_OP_BINOP(+=)
UNITTEST_OP_BINOP(-=)
UNITTEST_OP_BINOP(%=)
UNITTEST_OP_BINOP(*=)
UNITTEST_OP_BINOP(/=)
#undef UNITTEST_OP_BINOP
}
namespace boost { namespace test_tools {
using unittest_op::operator ==;
using unittest_op::operator !=;
using unittest_op::operator < ;
using unittest_op::operator > ;
using unittest_op::operator <=;
using unittest_op::operator >=;
using unittest_op::operator + ;
using unittest_op::operator - ;
using unittest_op::operator % ;
using unittest_op::operator * ;
using unittest_op::operator / ;
using unittest_op::operator +=;
using unittest_op::operator -=;
using unittest_op::operator %=;
using unittest_op::operator *=;
using unittest_op::operator /=;
using unittest_op::operator <<;
} }
class MyTypeWithDouble
{
public:
constexpr MyTypeWithDouble(double v = 0) : m(v) {}
MyTypeWithDouble& operator=(MyTypeWithDouble const&) noexcept = default;
constexpr MyTypeWithDouble(MyTypeWithDouble const&) noexcept = default;
private:
friend class unittest_op::access;
double m;
};
namespace unittest_op {
template<> class access::impl<MyTypeWithDouble> {
public:
typedef double result_type;
static result_type call(MyTypeWithDouble const& v) { return v.m; }
};
}
#define BOOST_TEST_MODULE MyTest
#include <boost/test/unit_test.hpp>
BOOST_AUTO_TEST_CASE(my_test)
{
MyTypeWithDouble v(4);
BOOST_CHECK_CLOSE(3.99, v, MyTypeWithDouble(0.1));
}
BOOST_AUTO_TEST_CASE(general_operator_invocations) // just a testbed to see the overloads are found and compile
{
MyTypeWithDouble v(4);
using namespace unittest_op; // we're not using the test_tools here
BOOST_CHECK(4.00000000000000001 == v);
BOOST_CHECK(4.000000000000001 != v);
#define UNITTEST_OP_BINOP(OP) { \
auto x = v OP static_cast<MyTypeWithDouble>(0.01); \
x = static_cast<MyTypeWithDouble>(0.01) OP v; \
x = v OP v; \
(void) x; \
}
UNITTEST_OP_BINOP(==)
UNITTEST_OP_BINOP(!=)
UNITTEST_OP_BINOP(+ )
UNITTEST_OP_BINOP(- )
//UNITTEST_OP_BINOP(% )
UNITTEST_OP_BINOP(* )
UNITTEST_OP_BINOP(/ )
UNITTEST_OP_BINOP(< )
UNITTEST_OP_BINOP(> )
UNITTEST_OP_BINOP(<=)
UNITTEST_OP_BINOP(>=)
-v == -v;
+v == +v;
}