I am trying to implement the Maybe monad from Haskell using the lambda functions in C++11 and templates. Here's what I have so far
#include<functional>
#include<iostream>
using namespace std;
template<typename T1>
struct Maybe
{
T1 data;
bool valid;
};
template<typename T1, typename T2>
Maybe<T2> operator>>=(Maybe<T1> t, std::function < Maybe<T2> (T1)> &f)
{
Maybe<T2> return_value;
if(t.valid == false)
{
return_value.valid = false;
return return_value;
}
else
{
return f(t.data);
}
}
int main()
{
Maybe<int> x = {5, true};
Maybe<int> y = {29, false};
auto z = [](int a) -> Maybe<int>
{
Maybe<int> s;
s.data = a+1;
s.valid = true;
return s;
};
Maybe<int> p = (x >>= z);
Maybe<int> q = (y >>= z);
cout<<p.data<<' '<<p.valid<<endl;
cout<<q.data<<' '<<q.valid<<endl;
}
When it comes to the actual >>= call, I am getting a compiler error saying that no match found for >>= operator. Is my understanding of C++11's lambda functions failing me here?
The type of a lambda isn't a specialization of std::function. It's some unamed type. There is a conversion to std::function, but that means type deduction won't work for it. So, in this call:
Maybe<int> p = (x >>= z);
The type T2 can't be deduced:
Maybe<T2> operator>>=(Maybe<T1> t, std::function < Maybe<T2> (T1)> &f)
Store the lambda in a std::function variable from the start, and it should work:
std::function < Maybe<int> (int)> z = [](int a) -> Maybe<int> { ... };
However, it's probably easier to accept any kind of function object. That way you can still use auto for the lambda.
template<typename T1, typename F>
typename std::result_of<F(T1)>::type
operator>>=(Maybe<T1> t, F&& f) {
... std::forward<F>(f)(t.data);
}
The following works for me: I use decltype to infer the type returned by the lambda:
template<typename T1, typename Func>
auto operator>>=(Maybe<T1> t, Func f) -> decltype(f(t.data))
{
decltype(f(t.data)) return_value;
if(t.valid == false)
{
return_value.valid = false;
return return_value;
}
else
{
return f(t.data);
}
}
EDIT
For type safety :
template<typename T1>
struct Maybe
{
T1 data;
bool valid;
static const bool isMaybe = true;
};
template<typename T1, typename Func>
auto operator>>=(Maybe<T1> t, Func f) -> decltype(f(t.data))
{
typedef decltype(f(t.data)) RT;
static_assert(RT::isMaybe, "F doesn't return a maybe");
...
Here's my maybe "monad" that I use quite often in my C++ projects (disclaimer: see the comments below). It's insofar more like the Haskell Maybe than your implementation as it only holds an object in the just case (points mobj on it), not wasting space if it's nothing. This also allows it to use of C++11 move semantics, to avoid unnecessary copies. The return types of fmap (fmapped member function) and >>= are deduced with decltype.
template<typename DataT>
class maybe;
template<typename DataT>
maybe<DataT> just(const DataT &obj);
struct nothing_object{nothing_object(){}};
const nothing_object nothing;
//template class objects of which may or may not contain some given
// data object. Inspired by Haskell's Maybe monad.
template<typename DataT>
class maybe {
DataT *obj;
public:
class iterator {
DataT *mobj;
explicit iterator(DataT *init):mobj(init){}
public:
iterator():mobj(nullptr){}
iterator(const iterator &cp):mobj(cp.mobj){}
bool operator!=(const iterator &other)const{return mobj!=other.mobj;}
DataT &operator*() const{return *mobj;}
iterator &operator++(){ mobj=nullptr; return *this; }
friend class maybe;
};
class const_iterator {
const DataT *mobj;
explicit const_iterator(const DataT *init):mobj(init){}
public:
const_iterator():mobj(nullptr){}
const_iterator(const const_iterator &cp):mobj(cp.mobj){}
bool operator!=(const const_iterator &other)const{return mobj!=other.mobj;}
const DataT &operator*() const{return *mobj;}
const_iterator &operator++(){ mobj=nullptr; return *this; }
friend class maybe;
};
iterator begin(){return iterator(obj);}
iterator end(){return iterator();}
const_iterator begin()const{return const_iterator(obj);}
const_iterator end()const{return const_iterator();}
const_iterator c_begin()const{return const_iterator(obj);}
const_iterator c_end()const{return const_iterator();}
bool is_nothing()const{return obj==nullptr;}
void make_nothing(){delete obj; obj=nullptr;}
bool is_just()const{return obj!=nullptr;}
template<typename CpDataT>
void with_just_assign(CpDataT &mdftg)const{if(obj) mdftg=*obj;}
DataT &from_just(){return *obj;}
DataT &operator*(){return *obj;}
const DataT &from_just()const{return *obj;}
const DataT &operator*()const{return *obj;}
template<typename CmpDataT>
bool operator==(const maybe<CmpDataT> &cmp)const{
return is_just()==cmp.is_just() && (is_nothing() || *obj==*cmp.obj); }
template<typename CmpDataT>
bool operator!=(const maybe<CmpDataT> &cmp)const{
return is_just()!=cmp.is_just() || (is_just() && *obj!=*cmp.obj); }
bool operator==(const nothing_object &n)const{return obj==nullptr;}
bool operator!=(const nothing_object &n)const{return obj!=nullptr;}
template<typename MpFnT>
auto fmapped(MpFnT f) const -> maybe<decltype(f(*obj))> {
return obj? just(f(*obj)) : nothing; }
template<typename MonadicFn>
auto operator>>=(MonadicFn f) const -> decltype(f(*obj)) {
return obj? f(*obj) : nothing; }
template<typename ReplaceDT>
auto operator>>(const maybe<ReplaceDT> &r) const -> maybe<ReplaceDT> {
return obj? r : nothing; }
auto operator>>(const nothing_object &n) const -> maybe<DataT> {
return nothing; }
maybe(const nothing_object &n):obj(nullptr){}
template<typename CpDataT>
explicit maybe(const CpDataT &cobj):obj(new DataT(cobj)){}
template<typename CpDataT>
maybe &operator=(const CpDataT &cobj){delete obj; obj=new DataT(cobj); return *this;}
template<typename CpDataT>
maybe(const maybe<CpDataT> &cp):obj(cp.is_just()?new DataT(cp.from_just()):nullptr){}
template<typename CpDataT>
maybe &operator=(const maybe<CpDataT> &cp){
delete obj; obj = cp.is_just()? new DataT(cp.from_just()) : nullptr; return *this;}
maybe(maybe<DataT> &&mv):obj(mv.obj){mv.obj=nullptr;}
maybe &operator=(maybe<DataT> &&mv) {
delete obj; obj=mv.obj; mv.obj=nullptr; return *this; }
~maybe(){delete obj;}
};
template<typename DataT>
auto just(const DataT &obj) -> maybe<DataT> {return maybe<DataT>(obj);}
template<typename MpFnT, typename DataT> // represents Haskell's <$> infix
auto operator^(MpFnT f, const maybe<DataT> &m) -> decltype(m.fmapped(f)) {
return m.fmapped(f);
}
template<typename DataT>
auto joined(const maybe<maybe<DataT>> &m) -> maybe<DataT> {
return m.is_just()? m.from_just() : nothing;
}
template<typename DataT>
auto maybe_yes(const std::pair<DataT,bool>& mbcst) -> maybe<DataT> {
return mbcst.second ? just(mbcst.first) : nothing;
}
template<typename DataT>
auto maybe_not(const std::pair<DataT,bool>& mbcst) -> maybe<DataT> {
return !mbcst.second ? just(mbcst.first) : nothing;
}
The somewhat strange-seeming begin and end iterators allow it to be used in C++11 range-based for loops:
maybe<int> a = just(7), b = nothing;
for (auto&i: a) std::cout << i;
for (auto&i: b) std::cout << i;
outputs only once 7.
Noticed that std::function have an empty state, we can have the following implementation
template<typename T>
class Maybe{
private:
Maybe(T t){
get = [t](){ return t; };
}
Maybe(){}
std::function<T ()> get;
public:
typedef T content_type;
template<typename WhenJust, typename WhenNothing>
auto on(WhenJust &&whenJust, WhenNothing &&whenNothing)
-> decltype(whenNothing()){
if(get==nullptr) return whenNothing();
else return whenJust(get());
}
template<typename U>
friend Maybe<U> just(U u);
template<typename U>
friend Maybe<U> nothing();
};
template<typename T>
Maybe<T> just(T t){
return Maybe<T>(t);
}
template<typename T>
Maybe<T> nothing(){
return Maybe<T>();
}
template<typename T, typename BinderFunction>
auto operator >>(Maybe<T> m, BinderFunction bind)
-> Maybe<typename decltype(bind(*((T*)nullptr)))::content_type> {
return m.on([bind](T v){
return bind(v);
},[](){
return nothing<typename decltype(bind(*((T*)nullptr)))::content_type>();
});
}
In this implementation, all factory methods are free (friend) functions, the >> operator (not to be confused with >> in Haskell, this is the equivalent of >>= with same associative) is also free, and even not a friend function. Also notice the on member function, this is used to force any client intended to use a Maybe instance must be prepared for both cases (Just or Nothing).
Here is an example of usage:
int main()
{
auto n = just(10) >> [](int j){ std::cout<<j<<" >> "; return just(j+10.5); }
>> [](double d){ std::cout<<d<<" >> "; return nothing<char>(); }
>> [](char c){ std::cout<<c; return just(10); }
;
n.on(
[](int i) { std::cout<<i; },
[]() { std::cout<<"nothing!"; });
std::cout << std::endl;
return 0;
}
The output is
10 >> 20.5 >> nothing!
My 5 cts.
Sample usage:
Maybe<string> m1 ("longlonglong");
auto res1 = m1 | lengthy | length;
lengthy and length are "monadic lambdas", i.e.
auto length = [] (const string & s) -> Maybe<int>{ return Maybe<int> (s.length()); };
Complete code:
// g++ -std=c++1y answer.cpp
#include <iostream>
using namespace std;
// ..................................................
// begin LIBRARY
// ..................................................
template<typename T>
class Maybe {
//
// note: move semantics
// (boxed value is never duplicated)
//
private:
bool is_nothing = false;
public:
T value;
using boxed_type = T;
bool isNothing() const { return is_nothing; }
explicit Maybe () : is_nothing(true) { } // create nothing
//
// naked values
//
explicit Maybe (T && a) : value(std::move(a)), is_nothing(false) { }
explicit Maybe (T & a) : value(std::move(a)), is_nothing(false) { }
//
// boxed values
//
Maybe (Maybe & b) : value(std::move(b.value)), is_nothing(b.is_nothing) { b.is_nothing = true; }
Maybe (Maybe && b) : value(std::move(b.value)), is_nothing(b.is_nothing) { b.is_nothing = true; }
Maybe & operator = (Maybe & b) {
value = std::move(b.value);
(*this).is_nothing = b.is_nothing;
b.is_nothing = true;
return (*this);
}
}; // class
// ..................................................
template<typename IT, typename F>
auto operator | (Maybe<IT> mi, F f) // chaining (better with | to avoid parentheses)
{
// deduce the type of the monad being returned ...
IT aux;
using OutMonadType = decltype( f(aux) );
using OT = typename OutMonadType::boxed_type;
// just to declare a nothing to return
Maybe<OT> nothing;
if (mi.isNothing()) {
return nothing;
}
return f ( mi.value );
} // ()
// ..................................................
template<typename MO>
void showMonad (MO m) {
if ( m.isNothing() ) {
cout << " nothing " << endl;
} else {
cout << " something : ";
cout << m.value << endl;
}
}
// ..................................................
// end LIBRARY
// ..................................................
// ..................................................
int main () {
auto lengthy = [] (const string & s) -> Maybe<string> {
string copyS = s;
if (s.length()>8) {
return Maybe<string> (copyS);
}
return Maybe<string> (); // nothing
};
auto length = [] (const string & s) -> Maybe<int>{ return Maybe<int> (s.length()); };
Maybe<string> m1 ("longlonglong");
Maybe<string> m2 ("short");
auto res1 = m1 | lengthy | length;
auto res2 = m2 | lengthy | length;
showMonad (res1);
showMonad (res2);
} // ()
Literally copy & pasting from Haskell style "Maybe" type & *chaining* in C++11
This is probably what you really want to achieve
#include <iostream>
#include <map>
#include <deque>
#include <algorithm>
#include <type_traits>
typedef long long int int64;
namespace monad { namespace maybe {
struct Nothing {};
template < typename T >
struct Maybe {
template < typename U, typename Enable = void >
struct ValueType {
typedef U * const type;
};
template < typename U >
struct ValueType < U, typename std::enable_if < std::is_reference < U >::value >::type > {
typedef typename std::remove_reference < T >::type * const type;
};
typedef typename ValueType < T >::type value_type;
value_type m_v;
Maybe(Nothing const &) : m_v(0) {}
struct Just {
value_type m_v;
Just() = delete;
explicit Just(T &v) : m_v(&v) {
}
};
Maybe(Just const &just) : m_v(just.m_v) {
}
};
Nothing nothing() {
return Nothing();
}
template < typename T >
Maybe < T > just(T &v) {
return typename Maybe < T >::Just(v);
}
template < typename T >
Maybe < T const > just(T const &v) {
return typename Maybe < T const >::Just(v);
}
template < typename T, typename R, typename A >
Maybe < R > operator | (Maybe < T > const &t, R (*f)(A const &)) {
if (t.m_v)
return just < R >(f(*t.m_v));
else
return nothing();
}
template < typename T, typename R, typename A >
Maybe < R > operator | (Maybe < T > const &t, Maybe < R > (*f)(A const &)) {
if (t.m_v)
return f(*t.m_v);
else
return nothing();
}
template < typename T, typename R, typename A >
Maybe < R > operator | (Maybe < T > const &t, R (*f)(A &)) {
if (t.m_v)
return just < R >(f(*t.m_v));
else
return nothing();
}
template < typename T, typename R, typename A >
Maybe < R > operator | (Maybe < T > const &t, Maybe < R > (*f)(A &)) {
if (t.m_v)
return f(*t.m_v);
else
return nothing();
}
template < typename T, typename R, typename... A >
Maybe < R > operator | (Maybe < T const > const &t, R (T::*f)(A const &...) const) {
if (t.m_v)
return just < R >(((*t.m_v).*f)());
else
return nothing();
}
template < typename T, typename R, typename... A >
Maybe < R > operator | (Maybe < T const > const &t, Maybe < R > (T::*f)(A const &...) const) {
if (t.m_v)
return just < R >((t.m_v->*f)());
else
return nothing();
}
template < typename T, typename R, typename... A >
Maybe < R > operator | (Maybe < T const > const &t, R (T::*f)(A const &...)) {
if (t.m_v)
return just < R >(((*t.m_v).*f)());
else
return nothing();
}
template < typename T, typename R, typename... A >
Maybe < R > operator | (Maybe < T const > const &t, Maybe < R > (T::*f)(A const &...)) {
if (t.m_v)
return just < R >((t.m_v->*f)());
else
return nothing();
}
template < typename T, typename A >
void operator | (Maybe < T > const &t, void (*f)(A const &)) {
if (t.m_v)
f(*t.m_v);
}
}}
struct Account {
std::string const m_id;
enum Type { CHECKING, SAVINGS } m_type;
int64 m_balance;
int64 withdraw(int64 const amt) {
if (m_balance < amt)
m_balance -= amt;
return m_balance;
}
std::string const &getId() const {
return m_id;
}
};
std::ostream &operator << (std::ostream &os, Account const &acct) {
os << "{" << acct.m_id << ", "
<< (acct.m_type == Account::CHECKING ? "Checking" : "Savings")
<< ", " << acct.m_balance << "}";
}
struct Customer {
std::string const m_id;
std::deque < Account > const m_accounts;
};
typedef std::map < std::string, Customer > Customers;
using namespace monad::maybe;
Maybe < Customer const > getCustomer(Customers const &customers, std::string const &id) {
auto customer = customers.find(id);
if (customer == customers.end())
return nothing();
else
return just(customer->second);
};
Maybe < Account const > getAccountByType(Customer const &customer, Account::Type const type) {
auto const &accounts = customer.m_accounts;
auto account = std::find_if(accounts.begin(), accounts.end(), [type](Account const &account) -> bool { return account.m_type == type; });
if (account == accounts.end())
return nothing();
else
return just(*account);
}
Maybe < Account const > getCheckingAccount(Customer const &customer) {
return getAccountByType(customer, Account::CHECKING);
};
Maybe < Account const > getSavingsAccount(Customer const &customer) {
return getAccountByType(customer, Account::SAVINGS);
};
int64 const &getBalance(Account const &acct) {
return acct.m_balance;
}
template < typename T >
void print(T const &v) {
std::cout << v << std::endl;
}
int main(int const argc, char const * const argv[]) {
Customers customers = {
{ "12345", { "12345", { { "12345000", Account::CHECKING, 20000 }, { "12345001", Account::SAVINGS, 117000 } } } }
, { "12346", { "12346", { { "12346000", Account::SAVINGS, 1000000 } } } }
};
getCustomer(customers, "12346") | getCheckingAccount | getBalance | &print < int64 const >;
getCustomer(customers, "12345") | getCheckingAccount | getBalance | &print < int64 const >;
getCustomer(customers, "12345") | getSavingsAccount | &Account::getId | &print < std::string const >;
// getCustomer(customers, "12345") | getSavingsAccount | [](Account &acct){ return acct.withdraw(100); } | &print < std::string const >;
}
Related
I'm trying to globally scale and add together callable/indexible objects (vectors in the abstract mathematical sense of the word).
That is to say, I'm trying to take linear combinations of objects that define operator[] or operator().
For example, I want to be able to do this:
LinearCombination<std::function<double(double, double)>> A([](double x, double y){
return 1+x+std::pow(x,2)+std::sin(y);
});
LinearCombination<std::function<double(double, double)>> B([](double x, double y){
return 1-x+std::cos(y);
});
A*= 2.5;
A += B;
std::cout << A(1.0,2.0) << std::endl;
My attempt
// ZERO ///////////////////////////////////////////////////////////////////////////////////////////
namespace hidden {
// tag dispatching: from https://stackoverflow.com/a/60248176/827280
template<int r>
struct rank : rank<r - 1> {};
template<>
struct rank<0> {};
template<typename T>
auto zero(rank<2>) -> decltype(static_cast<T>(0)) {
return static_cast<T>(0);
}
template<typename T>
auto zero(rank<1>) -> decltype(T::zero()) {
return T::zero();
}
template<typename T>
auto zero(rank<0>)->std::enable_if_t<
std::is_assignable<std::function<double(double,double)>, T>::value
, std::function<double(double,double)>> {
return []() {
return 0.0;
};
}
}
template<typename T>
auto zero() { return hidden::zero<T>(hidden::rank<10>{}); }
// LINEAR COMBINATION ///////////////////////////////////////////////////////////////////////////////////////////
template<typename V, typename C = double>
struct LinearCombination {
struct Term {
C coeff;
V vector;
// if V(x...) is defined
template<typename ...X>
auto operator()(X&&... x) const -> std::remove_reference_t<decltype(std::declval<V>()(std::forward<X>(x)...))> {
return vector(std::forward<X>(x)...) * coeff;
}
// if V[i] is defined
template<typename I>
auto operator[](I i) const -> std::remove_reference_t<decltype(std::declval<V>()[i])> {
return vector[i] * coeff;
}
};
std::vector<Term> terms;
LinearCombination() {} // zero
/*implicit*/ LinearCombination(V&& v) {
terms.push_back({ static_cast<C>(1), std::move(v) });
}
/*implicit*/ LinearCombination(Term&& term) {
terms.push_back(std::move(term));
}
LinearCombination<V, C>& operator+=(LinearCombination<V, C>&& other) {
terms.reserve(terms.size() + other.terms.size());
std::move(std::begin(other.terms), std::end(other.terms), std::back_inserter(terms));
other.terms.clear();
return *this;
}
LinearCombination<V, C>& operator*=(C multiplier) {
for (auto& term : terms) {
term.coeff *= multiplier;
}
return *this;
}
// if V(x...) is defined
template<typename ...X>
auto operator()(X&&... x) const
-> std::remove_reference_t<decltype(std::declval<V>()(std::forward<X>(x)...))> {
auto result = zeroVector()(std::forward<X>(x)...); <--------------- *** BAD FUNCTION CALL ***
*************************
for (const auto& term : terms) {
result += term(std::forward<X>(x)...);
}
return result;
}
// if V[i] is defined
template<typename I>
auto operator[](I i) const -> std::remove_reference_t<decltype(std::declval<V>()[i])> {
auto result = zeroVector()[i];
for (const auto& term : terms) {
result += term[i];
}
return result;
}
private:
static const V& zeroVector() {
static V z = zero<V>();
return z;
}
};
This compiles fine for me, but I get an exception on the indicated line (bad function call). Can you help?
This function:
template<typename T>
auto zero(rank<2>) -> decltype(static_cast<T>(0));
wins overload resolution against:
template<typename T>
auto zero(rank<0>)->std::enable_if_t<
std::is_assignable<std::function<double(double,double)>, T>::value
, std::function<double(double,double)>>;
This is because rank<2> is a better match for rank<10>{} than rank<0>, and also:
static_cast<std::function<double(double,double)>>(0)
is a valid expression.
That is, std::function has the following constructor:
function(std::nullptr_t) noexcept;
which makes it a viable choice for the 0 argument, and static_cast does considers constructors.
You end up with std::function<double(double,double)> initialized with 0 (empty), which leads to the exception when you attempt to invoke it.
I would like to understand where I am going wrong in trying to minimize the verbosity of my member functions template specialization. I get compilation errors when doing so rather arbitrarily. Here's the version that works, which hopefully will shed some light on what I am trying to achieve:
#include <iostream>
#include <type_traits>
typedef int i32;
template<class T>
struct rtvec
{
private:
T* e;
i32 d;
public:
rtvec(i32 d) : d(d), e(new T[d]) {}
//template<typename Args...>
//rtvec()
rtvec(const rtvec& in) : d(in.d), e(new T[in.d])
{
for (i32 i = 0; i < d; ++i)
at(i) = in.at(i);
}
rtvec(rtvec<typename std::remove_pointer_t<T>>& in) : d(in.dim()), e(new T[in.dim()])
{
for (i32 i = 0; i < d; ++i)
e[i] = &in.at(i);
}
~rtvec() { delete[] e; }
i32 dim() const { return d; }
template<typename U=T,
typename std::enable_if_t<std::is_same_v<U,T>>* = nullptr,
typename std::enable_if_t<!std::is_pointer_v<U>>* = nullptr>
inline T& at(i32 i)
{
return e[i];
}
template<typename U = T,
typename std::enable_if_t<std::is_same_v<U, T>>* = nullptr,
typename std::enable_if_t<std::is_pointer_v<U>>* = nullptr>
inline typename std::remove_pointer_t<T>& at(i32 i)
{
return *e[i];
}
};
int main()
{
rtvec<float> v(2);
v.at(0) = 1;
v.at(1) = 2;
rtvec<float*> p = v;
p.at(0) = 5;
std::cout << v.at(0) << " " << v.at(1) << "\n";
return 0;
}
Basically I am trying to make a runtime variable dimensional vector class, which when instantiated with a pointer, can be used as a sort of reference to a vector of the same type (more precisely I have several arrays of each of the coordinates of a set of points and I want to use the "reference" vector to be able to work with those as if they were ordered the other way around in memory).
When I try to simplify the code, however, by trying to remove what I perceive as an unnecessary typename U. I get the following compilation error in MSVC2017: std::enable_if_t<false,void>' : Failed to specialize alias template. Here's the less verbose version that I was aiming at achieving:
struct rtvec
{
private:
T* e;
i32 d;
public:
rtvec(i32 d) : d(d), e(new T[d]) {}
template<typename std::enable_if_t<!std::is_pointer_v<T>>* = nullptr>
rtvec(const rtvec& in) : d(in.d), e(new T[in.d])
{
for (i32 i = 0; i < d; ++i)
at(i) = in.at(i);
}
template<typename std::enable_if_t<std::is_pointer_v<T>>* = nullptr>
rtvec(rtvec<typename std::remove_pointer_t<T>>& in) : d(in.dim()), e(new T[in.dim()])
{
for (i32 i = 0; i < d; ++i)
e[i] = &in.at(i);
}
~rtvec() { delete[] e; }
i32 dim() const { return d; }
template<typename std::enable_if_t<!std::is_pointer_v<T>>* = nullptr>
inline T& at(i32 i)
{
return e[i];
}
template<typename std::enable_if_t<std::is_pointer_v<T>>* = nullptr>
inline typename std::remove_pointer<T>::type& at(i32 i)
{
return *e[i];
}
};
If I modify this a little, however, it does compile:
template<class T>
struct rtvec
{
private:
T* e;
i32 d;
public:
rtvec(i32 d) : d(d), e(new T[d]) {}
template<typename std::enable_if_t<!std::is_pointer_v<T>>* = nullptr>
rtvec(const rtvec& in) : d(in.d), e(new T[in.d])
{
for (i32 i = 0; i < d; ++i)
at(i) = in.at(i);
}
/*template<typename std::enable_if_t<std::is_pointer_v<T>>* = nullptr>
rtvec(rtvec<typename std::remove_pointer_t<T>>& in) : d(in.dim()), e(new T[in.dim()])
{
for (i32 i = 0; i < d; ++i)
e[i] = &in.at(i);
}*/
~rtvec() { delete[] e; }
i32 dim() const { return d; }
template<typename std::enable_if_t<!std::is_pointer_v<T>>* = nullptr>
inline T& at(i32 i)
{
return e[i];
}
/*template<typename std::enable_if_t<std::is_pointer_v<T>>* = nullptr>
inline typename std::remove_pointer<T>::type& at(i32 i)
{
return *e[i];
}*/
};
(as long as the part relating to the pointer is commented out in main too). I want to understand what is it that makes the 2nd code not compile.
The reason I am not directly specializing the class is that in my original implementation I have a lot of other member functions that will be equivalent between the two specializations which I do not want to repeat.
When I try to simplify the code, however, by trying to remove what I perceive as an unnecessary
Unfortunately (if I understand correctly) you have removed something that is necessary
If I understand correctly, you have simplified the following methods
template<typename U=T,
typename std::enable_if_t<std::is_same_v<U,T>>* = nullptr,
typename std::enable_if_t<!std::is_pointer_v<U>>* = nullptr>
inline T& at(i32 i)
{
return e[i];
}
template<typename U = T,
typename std::enable_if_t<std::is_same_v<U, T>>* = nullptr,
typename std::enable_if_t<std::is_pointer_v<U>>* = nullptr>
inline typename std::remove_pointer_t<T>& at(i32 i)
{
return *e[i];
}
as follows
template<typename std::enable_if_t<!std::is_pointer_v<T>>* = nullptr>
inline T& at(i32 i)
{
return e[i];
}
template<typename std::enable_if_t<std::is_pointer_v<T>>* = nullptr>
inline typename std::remove_pointer<T>::type& at(i32 i)
{
return *e[i];
}
Unfortunately SFINAE, over a template method, works only when uses tests (std::enable_if_t) based on template parameters of the method itself.
I mean: SFINAE doesn't works when the std::enable_if_t tests involve T (and only T) because T is a template parameter of the struct, not a template parameter of the method.
So you need the trick
typename U = T
that "transform" the T type in a template parameter of the method.
You can simplify a little removing the typename before std::enable_if_t because the "_t" is exactly typename (see the definition of std::enable_if_t)
Off topic: I'm not a language layer but, as far I know,
std::enable_if_t<std::is_same_v<U,T>>* = nullptr
isn't perfectly legal; I suggest to rewrite using int instead of void *
std::enable_if_t<std::is_same_v<U,T>, int> = 0
or maybe bool
std::enable_if_t<std::is_same_v<U,T>, bool> = true
or other integer types
Concluding, I suggest to rewrite your at() method as follows
template <typename U = T,
std::enable_if_t<std::is_same_v<U, T>, int> = 0,
std::enable_if_t<!std::is_pointer_v<U>, int> = 0>
inline T& at(i32 i)
{
return e[i];
}
template<typename U = T,
std::enable_if_t<std::is_same_v<U, T>, int> = 0,
std::enable_if_t<std::is_pointer_v<U>, int> = 0>
inline typename std::remove_pointer_t<T>& at(i32 i)
{
return *e[i];
}
There are several reasons and you need to provide the full compilation error. Regarding your code, you seem to use C++17.
For instance, in this code you are trying to return a reference to the object stored in the array, right?:
inline typename std::remove_pointer<T>::type& at(i32 i) {
return *e[i];
}
Can be replaced with a more STL-Like code:
using reference = T&;
using const_reference = const T&;
reference at(i32 i) {
return e[i];
}
const_reference at(i32 i) const {
return e[i];
}
Or use auto:
auto at(i32 i) const {
return e[i];
}
This is the way most of the STL containers work. For instance, if you access an std::vector<T*>, it will return a reference to a T*, not a reference to the data where T points.
Regarding the SFINAE technique you are using, I am not sure if it's properly written.
For instance, take a look into this post to find information about the proper ways to write the conditions for selecting constructors. Small sumary:
template <typename = typename std::enable_if<... condition...>::type>
explicit MyAwesomeClass(MyAwesomeClass<otherN> const &);
For instance, if you want to enable a constructor only for those instance that do not hold a pointer type:
template<typename = typename std::enable_if_t<!std::is_pointer_v<T>>>
explicit rtvec(const rtvec& in) : d(in.d), e(new T[in.d]) {
for (i32 i = 0; i < d; ++i)
at(i) = in.at(i);
}
Now, regarding the fact that you are using C++17, you can us constexpr if that will make your life much easy and handle the different situations. Something like this, I guess:
template <typename U>
explicit rtvec(const rtvec<U>& in) : d(in.d), e(new T[in.d]) {
for (i32 i = 0; i < d; ++i){
if constexpr (std::is_pointer<T>::value &&
std::is_pointer<U>::value) {
// ...
} else if constexpr (!std::is_pointer<T>::value &&
std::is_pointer<U>::value) {
// ...
} else {
// rest of possible combinations
}
}
}
How to handle an api which returns different data types for the same input data types?
Looking at the below example, apicall should return a date or a string depending on the input attribute:
#include <iostream>
#include <string>
using namespace std;
???? apicall(string datatype, string attribute)
{
// code
}
int main(int argc, char** argv)
{
string datatype = "Thomas"
string attribute = "bithday"
cout << apicall(datatype, attribute) << endl;
string datatype = "Thomas"
string attribute = "address"
cout << apicall(datatype, attribute) << endl;
}
What could be in place of ???? (apicall return datatype) and how to handle these cases?
I am trying to understand these concepts as my experience to date has been with duck typed scripting languages.
The ideal solution is to use a std::variant, which is a safe union type like.
This allows you to write the following:
using DateOrString = std::variant<DateType, std::string>;
DateOrString api_call(std::string, std::string) {
// you can return both DateType and std::string
}
// ...
auto result = api_call("", "");
auto& str = std::get<std::string>(result);
Unfortunately std::variant is a C++17 feature. However different compilers already support it.
As already has been suggested, boost has a variant class and you can use it with any C++ standard.
As last option, you may implement a "variant-like" class which handles both a date and a string. Your function should return it.
Here a demo how to quickly implement that kind of class.
Note that that class is safe because the type is checked at runtime.
As a variant object, your callee function should branch on the type, something like:
auto result = api_call(/*...*/);
if (result.is_string()) {
// result is a string
const auto& str = result.get_string();
} else {
// result is a date
const auto& date = result.get_date();
}
... returns different data types for the same input data types?
This is literally impossible. A function is defined with one (or zero) return types, and zero or more input parameter types.
The workarounds are:
Write a single function returning a variant type, such as std::variant in C++17, or Boost.Variant if that's not available.
Write multiple functions with different return types (the caller just has to choose the right one)
Invert control, so that instead of returning a value, you pass an object capable of processing all the required types:
struct APIHandler {
virtual ~APIHandler() {}
virtual void operator()(int) {}
virtual void operator()(string) {}
};
void apicall(string name, string attr, APIHandler &h) {
// dummy implementation
if (attr == "address") {
h("123 Woodford Road");
} else if (attr == "birthday") {
h(19830214);
}
}
// implement your type-specific logic here
struct MyHandler: APIHandler {
void operator()(int i) override {
cout << "got an int:" << i << '\n';
}
void operator()(string s) override {
cout << "got a string:" << s << '\n';
}
};
// and use it like:
MyHandler mh;
apicall("Thomas", "birthday", mh);
apicall("Thomas", "address", mh);
You want a std::variant in C++17 or a boost::variant or roll your own crude variant something like this:
constexpr std::size_t max() { return 0; }
template<class...Ts>
constexpr std::size_t max( std::size_t t0, Ts...ts ) {
return (t0<max(ts...))?max(ts...):t0;
}
template<class T0, class...Ts>
struct index_of_in;
template<class T0, class...Ts>
struct index_of_in<T0, T0, Ts...>:std::integral_constant<std::size_t, 0> {};
template<class T0, class T1, class...Ts>
struct index_of_in<T0, T1, Ts...>:
std::integral_constant<std::size_t,
index_of_in<T0, Ts...>::value+1
>
{};
struct variant_vtable {
void(*dtor)(void*) = 0;
void(*copy)(void*, void const*) = 0;
void(*move)(void*, void*) = 0;
};
template<class T>
void populate_vtable( variant_vtable* vtable ) {
vtable->dtor = [](void* ptr){ static_cast<T*>(ptr)->~T(); };
vtable->copy = [](void* dest, void const* src){
::new(dest) T(*static_cast<T const*>(src));
};
vtable->move = [](void* dest, void* src){
::new(dest) T(std::move(*static_cast<T*>(src)));
};
}
template<class T>
variant_vtable make_vtable() {
variant_vtable r;
populate_vtable<T>(&r);
return r;
}
template<class T>
variant_vtable const* get_vtable() {
static const variant_vtable table = make_vtable<T>();
return &table;
}
template<class T0, class...Ts>
struct my_variant {
std::size_t index = -1;
variant_vtable const* vtable = 0;
static constexpr auto data_size = max(sizeof(T0),sizeof(Ts)...);
static constexpr auto data_align = max(alignof(T0),alignof(Ts)...);
template<class T>
static constexpr std::size_t index_of() {
return index_of_in<T, T0, Ts...>::value;
}
typename std::aligned_storage< data_size, data_align >::type data;
template<class T>
T* get() {
if (index_of<T>() == index)
return static_cast<T*>((void*)&data);
else
return nullptr;
}
template<class T>
T const* get() const {
return const_cast<my_variant*>(this)->get<T>();
}
template<class F, class R>
using applicator = R(*)(F&&, my_variant*);
template<class T, class F, class R>
static applicator<F, R> get_applicator() {
return [](F&& f, my_variant* ptr)->R {
return std::forward<F>(f)( *ptr->get<T>() );
};
}
template<class F, class R=typename std::result_of<F(T0&)>::type>
R visit( F&& f ) & {
if (index == (std::size_t)-1) throw std::invalid_argument("variant");
static const applicator<F, R> table[] = {
get_applicator<T0, F, R>(),
get_applicator<Ts, F, R>()...
};
return table[index]( std::forward<F>(f), this );
}
template<class F,
class R=typename std::result_of<F(T0 const&)>::type
>
R visit( F&& f ) const& {
return const_cast<my_variant*>(this)->visit(
[&f](auto const& v)->R
{
return std::forward<F>(f)(v);
}
);
}
template<class F,
class R=typename std::result_of<F(T0&&)>::type
>
R visit( F&& f ) && {
return visit( [&f](auto& v)->R {
return std::forward<F>(f)(std::move(v));
} );
}
explicit operator bool() const { return vtable; }
template<class T, class...Args>
void emplace( Args&&...args ) {
clear();
::new( (void*)&data ) T(std::forward<Args>(args)...);
index = index_of<T>();
vtable = get_vtable<T>();
}
void clear() {
if (!vtable) return;
vtable->dtor( &data );
index = -1;
vtable = nullptr;
}
~my_variant() { clear(); }
my_variant() {}
void copy_from( my_variant const& o ) {
if (this == &o) return;
clear();
if (!o.vtable) return;
o.vtable->copy( &data, &o.data );
vtable = o.vtable;
index = o.index;
}
void move_from( my_variant&& o ) {
if (this == &o) return;
clear();
if (!o.vtable) return;
o.vtable->move( &data, &o.data );
vtable = o.vtable;
index = o.index;
}
my_variant( my_variant const& o ) {
copy_from(o);
}
my_variant( my_variant && o ) {
move_from(std::move(o));
}
my_variant& operator=(my_variant const& o) {
copy_from(o);
return *this;
}
my_variant& operator=(my_variant&& o) {
move_from(std::move(o));
return *this;
}
template<class T,
typename std::enable_if<!std::is_same<typename std::decay<T>::type, my_variant>{}, int>::type =0
>
my_variant( T&& t ) {
emplace<typename std::decay<T>::type>(std::forward<T>(t));
}
};
then your code looks like:
variant<string, int> apicall(string datatype, string attribute)
{
if (datatype > attribute) return string("hello world");
return 7;
}
int main()
{
string datatype = "Thomas"
string attribute = "bithday"
apicall(datatype, attribute).visit([](auto&&r){
cout << r << endl;
});
string datatype = "Thomas"
string attribute = "address"
apicall(datatype, attribute).visit([](auto&& r){
cout << r << endl;
});
}
with whatever visit or apply_visitor free function or method your particular variant supports.
This gets much more annoying in C++11 as we don't have generic lambdas.
You could use a variant, but it's up to the caller site to check the results. Boost and std defines two variant types, i.e. std::variant and std::any.
I'm studying c++ templates and reading <<C++ Templates: The Complete Guide>>. I don't understand the flowing about expression template:
The code as following:
//exprarray.h
#include <stddef.h>
#include <cassert>
#include "sarray.h"
template<typename T>
class A_Scale
{
public:
A_Scale(T const& t):value(t){}
T operator[](size_t) const
{
return value;
}
size_t size() const
{
return 0;
}
private:
T const& value;
};
template<typename T>
class A_Traits
{
public:
typedef T const& exprRef;
};
template<typename T>
class A_Traits<A_Scale<T> >
{
public:
typedef A_Scale<T> exprRef;
};
template<typename T,typename L1,typename R2>
class A_Add
{
private:
typename A_Traits<L1>::exprRef op1;
typename A_Traits<R2>::exprRef op2;
public:
A_Add(L1 const& a,R2 const& b):op1(a),op2(b)
{
}
T operator[](size_t indx) const
{
return op1[indx] + op2[indx];
}
size_t size() const
{
assert(op1.size()==0 || op2.size()==0 || op1.size() == op2.size());
return op1.size() != 0 ? op1.size() : op2.size();
}
};
template<typename T,typename L1,typename R2>
class A_Mul
{
private:
typename A_Traits<L1>::exprRef op1;
typename A_Traits<R2>::exprRef op2;
public:
A_Mul(L1 const& a,R2 const& b):op1(a),op2(b)
{
}
T operator[](size_t indx) const
{
return op1[indx] * op2[indx];
}
size_t size() const
{
assert(op1.size()==0 || op2.size()==0 || op1.size() == op2.size());
return op1.size() != 0 ? op1.size():op2.size();
}
};
template<typename T,typename Rep = SArray<T> >
class Array
{
public:
explicit Array(size_t N):expr_Rep(N){}
Array(Rep const& rep):expr_Rep(rep){}
Array& operator=(Array<T> const& orig)
{
assert(size() == orig.size());
for (size_t indx=0;indx < orig.size();indx++)
{
expr_Rep[indx] = orig[indx];
}
return *this;
}
template<typename T2,typename Rep2>
Array& operator=(Array<T2,Rep2> const& orig)
{
assert(size() == orig.size());
for (size_t indx=0;indx<orig.size();indx++)
{
expr_Rep[indx] = orig[indx];
}
return *this;
}
size_t size() const
{
return expr_Rep.size();
}
T operator[](size_t indx) const
{
assert(indx < size());
return expr_Rep[indx];
}
T& operator[](size_t indx)
{
assert(indx < size());
return expr_Rep[indx];
}
Rep const& rep() const
{
return expr_Rep;
}
Rep& rep()
{
return expr_Rep;
}
private:
Rep expr_Rep;
};
template<typename T,typename L1,typename R2>
Array<T,A_Add<T,L1,R2> >
operator+(Array<T,L1> const& a,Array<T,R2> const& b)
{
return Array<T,A_Add<T,L1,R2> >(A_Add<T,L1,R2>(a.rep(),b.rep()));
}
template<typename T,typename L1,typename R2>
Array<T,A_Mul<T,L1,R2> >
operator*(Array<T,L1> const& a,Array<T,R2> const& b)
{
return Array<T,A_Mul<T,L1,R2> >(A_Mul<T,L1,R2>(a.rep(),b.rep()));
}
template<typename T,typename R2>
Array<T,A_Mul<T,A_Scale<T>,R2> >
operator*(T const& a,Array<T,R2> const& b)
{
return Array<T,A_Mul<T,A_Scale<T>,R2> >(A_Mul<T,A_Scale<T>,R2>(A_Scale<T>(a),b.rep()));
}
The test code:
//test.cpp
#include "exprarray.h"
#include <iostream>
using namespace std;
template <typename T>
void print (T const& c)
{
for (int i=0; i<8; ++i) {
std::cout << c[i] << ' ';
}
std::cout << "..." << std::endl;
}
int main()
{
Array<double> x(1000), y(1000);
for (int i=0; i<1000; ++i) {
x[i] = i;
y[i] = x[i]+x[i];
}
std::cout << "x: ";
print(x);
std::cout << "y: ";
print(y);
x = 1.2 * x;
std::cout << "x = 1.2 * x: ";
print(x);
x = 1.2*x + x*y;
std::cout << "1.2*x + x*y: ";
print(x);
x = y;
std::cout << "after x = y: ";
print(x);
return 0;
}
My questions is why A_Traits for A_Scale is by value not by reference.
template<typename T>
class A_Traits
{
public:
typedef T const& exprRef;
};
template<typename T>
class A_Traits<A_Scale<T> >
{
public:
typedef A_Scale<T> exprRef;
};
The reason from the book as following:
This is necessary because of the following: In general, we can declare them to be references because most temporary nodes are bound in the top-level expression and therefore live until the end of the evaluation of that complete expression. The one exception are the A_Scalar nodes. They are bound within the operator functions and might not live until the end of the evaluation of the complete expression. Thus, to avoid that the members refer to scalars that don't exist anymore, for scalars the operands have to get copied "by value."
More detail please refer to the chapter 18 of C++ Templates: The Complete Guide
Consider, for example, the right hand side of
x = 1.2*x + x*y;
What the quote says is that this is composed of two different categories.
The heavy array x and y objects are not defined within this expression, but rather before it:
Array<double> x(1000), y(1000);
So, as you build expressions using them, you don't have to worry whether they're still alive - they were defined beforehand. Since they're heavy, you want to capture them by reference, and, fortunately, their lifetime makes that possible.
Conversely, the lightweight A_Scale objects are generated within the expression (e.g., implicitly by the 1.2 above). Since they're temporaries, you have to worry about their lifetime. Since they're lightweight, it's not a problem.
That's the rationale for the traits class differentiating between them: the former are by reference, and the latter are by value (they are copied).
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