concept_check and B&N trick

I ran into problems using BOOST_CLASS_REQUIRES in conjunction with the base class in the Barton-Nackman trick/Curiously recursive template technique. I want to require functions that take the derived type as an argument. So I declare a variable of the derived at struct level in the concept class, but then g++ (2.95) complains about it being an invalid use of undefined type... forward declaration... . Is there a way to make this work? If so, does anyone have/know of an example I can see how to order things to make it work? Thanks, E

Eric Ford wrote:
I want to require functions that take the derived type as an argument. So I declare a variable of the derived at struct level in the concept class, but then g++ (2.95) complains about it being an invalid use of undefined type... forward declaration... . Is there a way to make this work? If so, does anyone have/know of an example I can see how to order things to make it work?
Please show us complete source code of what you want to achieve, plus the exact error messages gcc produces. People may want to check if different compilers yield other error messages. Jens Maurer

Eric Ford wrote:
I want to require functions that take the derived type as an argument. So I declare a variable of the derived at struct level in the concept class, but then g++ (2.95) complains about it being an invalid use of undefined type... forward declaration... . Is
--- In Boost-Users@y..., Jens Maurer <Jens.Maurer@g...> wrote: there a
way to make this work? If so, does anyone have/know of an example I can see how to order things to make it work?
Please show us complete source code of what you want to achieve, plus the exact error messages gcc produces. People may want to check if different compilers yield other error messages.
Ok. I tried to trim some of the fat to ease study, but don't have time to reduce it to the simplest example. I did put it all in one file for easy testing. If you comment out the #define CAUSE_GCC_FAILURE, then it works for me. #include<cmath> #include<cstdio> #include<utility> #include<iostream> #include<boost/cast.hpp> #include<boost/operators.hpp> #include<boost/concept_check.hpp> #define CAUSE_GCC_FAILURE template<class T> struct ArithmeticConcept { void constraints() { boost::function_requires<boost::AssignableConcept<T> >(); boost::function_requires<boost::EqualityComparableConcept<T> >(); boost::function_requires<boost::ComparableConcept<T> >(); boost::function_requires<boost::PlusOpConcept<T,T,T> >(); boost::function_requires<boost::SubtractOpConcept<T,T,T> >(); boost::function_requires<boost::TimesOpConcept<T,T,T> >(); boost::function_requires<boost::DivideOpConcept<T,T,T> >(); }; }; template<class ScaledLeafClass, class UnscaledT > class scaled_num; // This seems to have problems with the Curiously Recursive Template Technique / Barton-Nackman Trick template<class T1, class T2> struct scaling_leaf_class_concept { typedef T1 scaled_type; typedef T2 unscaled_type; typedef bool sign_type; #ifdef CAUSE_GCC_FAILURE scaled_type scaled; #endif unscaled_type unscaled; void constraints() { boost::function_requires<ArithmeticConcept<unscaled_type> >(); // T1::scale(unscaled_type()); // T1::unscale(scaled_type()); // scaled_type().unscale(); // scaled_type().operator(); // sign_type b4 = scaled.sign(); }; }; // base class provides default functionality // requires access to functions scale and unscale // might be nice if it added a test to see if there would be domain/range problems template<class ScaledLeafClass, class UnscaledT > class scaled_num : boost::addable<ScaledLeafClass>, boost::addable<ScaledLeafClass,UnscaledT>, boost::subtractable<ScaledLeafClass>, boost::subtractable<ScaledLeafClass,UnscaledT>, boost::multipliable<ScaledLeafClass>, boost::multipliable<ScaledLeafClass,UnscaledT>, boost::dividable<ScaledLeafClass>, boost::dividable<ScaledLeafClass,UnscaledT>, boost::equality_comparable<ScaledLeafClass>, boost::equality_comparable<ScaledLeafClass,UnscaledT>, boost::less_than_comparable<ScaledLeafClass>, boost::less_than_comparable<ScaledLeafClass,UnscaledT> { public: typedef ScaledLeafClass leaf_type; typedef UnscaledT unscaled_type; typedef leaf_type scaled_type; typedef scaled_num<leaf_type,unscaled_type> self_type; typedef bool sign_type; BOOST_CLASS_REQUIRES2( ScaledLeafClass, UnscaledT, scaling_leaf_class_concept ); BOOST_CLASS_REQUIRES( UnscaledT, ArithmeticConcept ); public: // for accessing leaf_type = scaled_type, ro from outside leaf_type const& as_leaf() const { return static_cast<leaf_type const&>(*this); } protected: // for accessing leaf_type = scaled_type, rw internally leaf_type& as_leaf() { return static_cast<leaf_type&>(*this); } public: // here's where the three functions required in leaf_type are called static scaled_type scale(unscaled_type x) { return leaf_type::scale(x); }; static unscaled_type unscale( scaled_type x) { return x.as_leaf().unscale(x); }; unscaled_type unscale() const { return as_leaf().unscale(as_leaf()); }; public: unscaled_type operator()() const { return unscale(); }; // this provides explicit conversion to the unscaled type operator unscaled_type() const { return unscale(); }; // this provides implicit conversion to the unscaled_type, necessary for seemlessly replacing numerical types, but somewhat dangerous // operators // operators for the default dumb way to do things leaf_type& operator=(const leaf_type& a) { this->as_leaf() = a.as_leaf(); return as_leaf(); }; leaf_type& operator-() { *this = scale(-unscale()); return this; }; leaf_type& operator+=(const leaf_type& a) { *this = scale(unscale()+a.unscale()); return as_leaf(); }; leaf_type& operator-=(const leaf_type& a) { *this = scale(unscale()-a.unscale()); return as_leaf(); }; leaf_type& operator*=(const leaf_type& a) { *this = scale(unscale()*a.unscale()); return as_leaf(); }; leaf_type& operator/=(const leaf_type& a) { *this = scale(unscale()/a.unscale()); return as_leaf(); }; leaf_type& operator+=(const unscaled_type& a) { *this = scale(unscale()+a); return as_leaf(); }; leaf_type& operator-=(const unscaled_type& a) { *this = scale(unscale()-a); return as_leaf(); }; leaf_type& operator*=(const unscaled_type& a) { *this = scale(unscale()*a); return as_leaf(); }; leaf_type& operator/=(const unscaled_type& a) { *this = scale(unscale()/a); return as_leaf(); }; sign_type sign() const { return (operator()()<0); }; bool operator==(const leaf_type& a) const { return(unscale()==a.unscale()); }; bool operator<(const leaf_type& a) const { return(unscale()<a.unscale()); }; bool operator==(const unscaled_type& a) { return(unscale()==a); }; bool operator<(const unscaled_type& a) { return(unscale()<a); }; bool operator>(const unscaled_type& a) { return(unscale()>a); }; }; template<class T1,class T2> inline ostream& operator<<(ostream& os, scaled_num<T1,T2> const& x) { return (os << x()); } // Concept for tags: Specify base and types template<class T> struct log_base_tag_concept { typename T::internal_type s; typename T::return_type u; typename T::log_base_type b; typename T::sign_type sign; void constraints() { boost::function_requires<ArithmeticConcept<typename T::return_type>
(); BOOST_STATIC_ASSERT(T::Base>0); b = T::Base; s = T::scale_helper(u); u = T::unscale_helper(s); }; };
template<class T> struct log_e_tag { typedef T return_type; typedef T internal_type; typedef T log_base_type; typedef bool sign_type; BOOST_STATIC_CONSTANT(log_base_type, Base = M_E); static internal_type scale_helper(const T& x) { return std::log(x); }; static T unscale_helper(const internal_type& x) { return std::exp(x); }; }; // Example of one scaling function that could be used... template<class UnscaledType, class TraitsT = log_e_tag<UnscaledType> > class log_num; template<class UnscaledType, class TraitsT > class log_num : public scaled_num< log_num<UnscaledType,TraitsT>, UnscaledType > { BOOST_CLASS_REQUIRES( UnscaledType, ArithmeticConcept ); BOOST_CLASS_REQUIRES(TraitsT, log_base_tag_concept); public: // typedefs typedef TraitsT traits; typedef log_num<UnscaledType,traits > self_type; typedef scaled_num< self_type, UnscaledType > base_type; typedef typename traits::sign_type sign_type; typedef typename traits::internal_type internal_type; public: // constructors explicit log_num(unscaled_type Value) : mValue(scale_internal(abs(Value))), mSign(Value<0) {}; // from unscaled log_num(internal_type Value, sign_type Sign ) : mValue(Value), mSign(Sign) {}; // from scaled protected: // data (internal, I should hope it has some data) internal_type mValue; sign_type mSign; // true = negative, false = positive protected: // scaling function helpers (optional, but generally helpful) static internal_type scale_internal(const unscaled_type& x) { return traits::scale_helper(x); }; static unscaled_type unscale_internal(const internal_type& x) { return traits::unscale_helper(x); }; template<class T1, class T2> bool log_num_xor(const T1& a, const T2& b){ return ((a)&&(!b))||((!a)&&(b)); } public: // data inspectors (optional, but generally desired) const internal_type& get_scaled_value() const { return mValue; }; sign_type get_sign() const { return mSign; }; typename TraitsT::log_base_type get_base() const { return TraitsT::Base; }; protected: // data mutators (internal, optional) const internal_type& scaled_value() const { return mValue; }; internal_type& scaled_value() { return mValue; }; sign_type sign() const { return mSign; }; sign_type& sign() { return mSign; }; public: // scaling functions (REQUIRED) static scaled_type scale(unscaled_type x) { return self_type(x); }; static unscaled_type unscale(const scaled_type& x) { if(x.sign()) return -unscale_internal(x.scaled_value()); else return unscale_internal(x.scaled_value()); }; unscaled_type unscale() const { return unscale(*this); }; // have to provide since overloading other unscale }; // specialize functions which can be optimized for this particular scaling function template<class TagT, class T > void log_num_test(T x, T y, T z) { log_num<T,TagT> bnx(x); log_num<T,TagT> bny = log_num<T,TagT>(y); log_num<T,TagT> bnz(-1); cout << "start: x = " << bnx << " y = " << bny << " z = " << bnz << endl; bnz = bnx + bny; cout << "z=x+y: x = " << bnx << " y = " << bny << " z = " << bnz << endl; bnz = bnx * bny; cout << "z=x*y: x = " << bnx << " y = " << bny << " z = " << bnz << endl; bnz = bnx; cout << "z=x: x = " << bnx << " y = " << bny << " z = " << bnz << endl; cout << "z==/</>x: " << (bnz==bnx) << " " << (bnz<bnx) << " " << (bnz>bnx) << endl; bnz *= bnx; cout << "z*=x: x = " << bnx << " y = " << bny << " z = " << bnz << endl; cout << "z==/</>x: " << (bnz==bnx) << " " << (bnz<bnx) << " " << (bnz>bnx) << endl; bnz *= x; cout << "z*=x: x = " << bnx << " y = " << bny << " z = " << bnz << endl; }; int main(int argc, char **argv) { float x = 0.5, y=18.; if(argc>1) { sscanf(argv[1],"%f",&x); } if(argc>2) { sscanf(argv[2],"%f",&y); } cout << "float, log_e" << endl; log_num_test<log_e_tag<float>,float>(x,y,-1); return 0; }
participants (2)
-
Eric Ford
-
Jens Maurer