FunctoidsInCpp can be modified to use the concepts defined in ConceptCpp in place of the Sig structure to determine the return type. This code has been tested using ConceptGcc. Here are two versions of the same code, one without and one with concepts. Note that the code is not complete, e.g. both versions call through Curryable2Helper to resolve overloading of the operator() with two arguments. This call returns the type defined by the appropriate concept map. One advantage of doing it in this way is that the concept maps are reusable with different classes, rather than defined within a particular class template. -- JohnFletcher
Original Code
Version of the FunctoidsInCpp code for a two parameter polymorphic functoid not using ConceptCpp.
template <class F> class Full2 : public SmartFunctoid2 { F f; public: Full2() : f() {} Full2( const F& ff ) : f(ff) {} template <class X, class Y=void> struct Sig : public FunType<typename F::template Sig<X,Y>::Arg1Type, typename F::template Sig<X,Y>::Arg2Type, typename RT<F,X,Y>::ResultType> {}; template <class X> struct Sig<X,void> : public FunType<X,Full1<impl::binder1of2<F,X> > > {}; template <class Y> struct Sig<AutoCurryType,Y> : public FunType<AutoCurryType,Y,Full1<impl::binder2of2<F,Y> > > {}; template <class X> struct Sig<X,AutoCurryType> : public FunType<X,AutoCurryType,Full1<impl::binder1of2<F,X> > > {}; template <class X> inline typename Sig<X>::ResultType operator()( const X& x ) const { return makeFull1( impl::binder1of2<F,X>(f,x) ); } template <class X, class Y> inline typename Sig<X,Y>::ResultType operator()( const X& x, const Y& y ) const { // need partial specialization, so defer to a class helper return impl::Curryable2Helper<typename Sig<X,Y>::ResultType,F,X,Y>::go(f,x,y); } };
The same code using concepts.
auto concept ConstCallable2<typename F, typename X, typename Y = Nothing> { typename result_type; requires CopyConstructible<result_type>; result_type operator()(const F&, const X&, const Y& y); }; /* These concept maps are the equivalent of the different Sig definitions. */ template <class F,class X> concept_map ConstCallable2<F,X> { typedef Full1<impl::binder1of2<F,X> > result_type; }; template <class F,class X> concept_map ConstCallable2<F,X,AutoCurryType> { typedef Full1<impl::binder1of2<F,X> > result_type; }; template <class F,class Y> concept_map ConstCallable2<F,AutoCurryType,Y> { typedef Full1<impl::binder2of2<F,Y> > result_type; }; template <class F> class Full2 : public SmartFunctoid2 { F f; public: Full2() : f() {} Full2( const F& ff ) : f(ff) {} template <class X> inline ConstCallable2<F,X>::result_type operator()( const X& x ) const { return makeFull1( impl::binder1of2<F,X>(f,x) ); } template <class X, class Y> inline ConstCallable2<F,X,Y>::result_type operator()( const X& x, const Y& y ) const { // need partial specialization, so defer to a class helper return impl::Curryable2Helper<typename ConstCallable2<F,X,Y>::result_type,F,X,Y>::go(f,x,y); } };
Example of usage.
This is the code within FC++(FunctoidsInCpp) which enables code using e.g. the FC++ object minus to handle calls such as
minus(_,1)(2); // minus(_,1) returns a function which needs another argument. minus(2,_)(1); // minus(2,_) returns a function which needs another argument.which need to be distinguished at compile time from
minus(2,1); // returns a resultwhere the _ character is an object of type AutoCurryType. Both versions of the code use the type of the argument to guide the selection of the return type from the two argument call. The actual return types are constructed in template specialisations of Curryable2Helper which are not shown here. They are the same whether or not concepts are used.
Single parameter calls can also be used
minus(2)(1); // minus(2) returns a function which needs another argument.In this case the concept map has one template parameter and the other is supplied by the default type Nothing.
-- JohnFletcher
CategoryCpp CategoryCppTemplates CategoryFunctionalProgramming CategoryConcepts