Re: [sigc] Fixing lambda bugs



Murray,

The key problem is the usual problem with references and const references.
Although I and probably a few others have suggested adding a template 
pattern to pass arguments through as reference without forcing a type check
(ie.  template <class &T> void foo(T t) {}  where T will be any reference, const
reference, volitile const reference, etc.), it was summarily rejected.  However, 
recently I looked through the proposed changes for the next version.  
Still no action as far as I can see.  However, one of changes did inspire me to 
update my print format class which got me working on the problem again.  That
was that I could use a variodic template pattern.   As part of that work I built a 
wrapper to work around the problem.  

I have two options that can fix the defect with argument copying, some of the 
operator returns not promoting properly, and improper function of equality operators.  
1) Apply the same pattern in my variodic template library to this problem.  
2) Convert the lambda library to use the variodic template library.  

In either case the outward library interface should have no changes what-so-ever.
However, the second option will gut the library entirely.  I am slightly more interested
in the second than the first in that I don't remember (or perhaps there have been 
patches applied) such that figuring out where to perform the surgery for option 1 will
take a while.  Then again option 2 may have similar problems.  

In either case if I cut too deeply in the internals it will break users that wrote a function
that a lambda without a closure (a very bad idea) or a user that wrote their own internal
to use lambda (some of the extension libraries may have done something like this).  

To give you a better idea which way may be best I have inclosed a sample for the 
variodic template library so that this isn't quite so cryptic.  Significant differences would
be that variodic functions use execute to avoid that sun bug with operator() and lambda
would get variodic_function<T_func> as a base class.

The type promotion I have working in practice, but I will need to plan how to 
apply it current lambda library.  I have also attached a sample of that.  This one may have
architecture issues as int*int is not actually an int but may be a int64_t so there are 
a fair number of edge cases to consider.  

Does this help clarify?

--Karl

 -------------- Original message ----------------------
From: Murray Cumming <murrayc murrayc com>
> On Fri, 2008-12-12 at 02:28 +0000, kenelson8 comcast net wrote:
> > Hello all,
> > 
> > I see there are some outstanding bugs in the lambda section.  I think that I 
> > can fix some of them along with some of the FIXME sections, however, I 
> > am concerned with the level of changes that may be required.  Is anyone
> > using the lambda?
> 
> Probably, yes.
> 
> >   Also is anyone connecting into the internal classes which
> > will likely get broken by a big change?
> 
> Can you be more specific? Then we can judge what a change there would
> affect. If it's obviously a private internal thing then there's no
> problem with breaking it.
> 
> Murray.
> 

=======================================================

#include <iostream>

struct variodic0
{
  typedef variodic0 next_type;
  typedef void value1_type;
  next_type& next() { return *this; }
};
variodic0 variodic0_instance; 

template <class T1>
struct variodic1 {
  typedef variodic0 next_type;
  typedef T1 value1_type;
  variodic1(value1_type& p1)
    : value_(p1) {}
  value1_type& value() const {return value_; }
  value1_type& value1() const { return value_ ;}
  next_type& next() const { return variodic0_instance; }
private:
  value1_type& value_;
};

template <class T1, class T2>
struct variodic2 {
  typedef variodic1<T2> next_type;
  typedef T1 value1_type;
  typedef T2 value2_type;
  variodic2(T1& p1, T2& p2)
    : value_(p1), next_(p2){}
  value1_type& value() const {return value_; }
  value1_type& value1() const {return value_; }
  value2_type& value2() const {return next_.value1(); }
  next_type &next() const {return const_cast<next_type&>(next_); }

private:
  value1_type& value_;
  next_type    next_;
};

template <class T1, class T2, class T3>
struct variodic3 {
  typedef variodic2<T2,T3> next_type;
  typedef T1 value1_type;
  typedef T2 value2_type;
  typedef T3 value3_type;

  variodic3(T1& p1, T2& p2, T3& p3)
    : value_(p1), next_(p2,p3){}
  value1_type& value() const {return value_; }
  value1_type& value1() const {return value_; }
  value2_type& value2() const {return next_.value1(); }
  value3_type& value3() const {return next_.value2(); }
  next_type &next() const {return const_cast<next_type&>(next_); }

private:
  value1_type& value_;
  next_type    next_;
};

/* What does a variodic_function require?

   class myfunc_implementation
   {
     public:
       // 1) This indicates the result if called with 0 arguments
       typedef void result_type;

       // 2) This indicates the type when multople arguments are called
       //   This can have specialization if the type is variable
       template <typename var_args> struct deduce_result_type { typedef void typ
e; }

       // 3) This is the front end for the call
       // specialize this for different number of arguments
       template <typename var_args>
       deduce_result_type<V>::type execute(const var_args& v);
   };

   // 4) we create a concrete type instance
   //   (often the instance will be in a file and thus external in the header)
   extern variodic_function<myfunc_implementation> myfunc;
*/

template<class T_f>
struct variodic_function {
  typename T_f::result_type
  operator()();

  // we can keep the const for a few layers
  // 2 required
  template<class T1>
  typename T_f::template deduce_result_type<variodic1<T1> >::type
  operator()(T1& t1)
  { return func_.execute(variodic1<T1>(t1)); }
  template<class T1>
  typename T_f::template deduce_result_type<variodic1<T1> >::type
  operator()(const T1& t1)
  { return func_.execute(variodic1<const T1>(t1)); }

  // 2^2=4 required
  template<class T1, class T2>
  typename T_f::template deduce_result_type<variodic2<T1,T2> >::type
  operator()(T1& t1, T2& t2)
  { return func_.execute(variodic2<T1,T2>(t1,t2)); }
  template<class T1, class T2>
  typename T_f::template deduce_result_type<variodic2<T1,T2> >::type
  operator()(T1& t1, const T2& t2)
  { return func_.execute(variodic2<T1,const T2>(t1,t2)); }
  template<class T1, class T2>
  typename T_f::template deduce_result_type<variodic2<T1,T2> >::type
  operator()(const T1& t1, T2& t2)
  { return func_.execute(variodic2<const T1,T2>(t1,t2)); }
  template<class T1, class T2>
  typename T_f::template deduce_result_type<variodic2<T1,T2> >::type
  operator()(const T1& t1, const T2& t2)
  { return func_.execute(variodic2<const T1,const T2>(t1,t2)); }


  // But eventually it just doesn't work as we would need too many templates
  // this hack works, but has the downside that type deduction may be a problem 
  // and overloading  of foo(int, char, const string&) and foo(int, char, T&) would always
  // chose the latter (though this type of overload is fairly bad practice) 
  template<class T1, class T2, class T3>
  typename T_f::template deduce_result_type<variodic3<T1,T2,T3> >::type
  operator()(const T1& t1, const T2& t2, const T3& t3)
  { return func_.execute(variodic3<T1,T2,T3>(const_cast<T1&>(t1),const_cast<T2&>
(t2),const_cast<T3&>(t3))); }

private:
  T_f func_;
};

template<class T_f>
typename T_f::result_type variodic_function<T_f>::operator()()
{ return func_.execute(variodic0()); }

// Use this if the implementation returns the same thing regardless
template <class T_R>
struct variodic_implementation_returns
{
  typedef T_R result_type;
  template <class T_V> struct deduce_result_type { typedef T_R type; };
};


/***********************************/

// Test the variodic functionality
//  (idea shamelessly stolen from the variodic template example in new standard)

struct qprintf_t : public variodic_implementation_returns<void>
{

  // variodic function require this method
  template <class var>
  void execute(const var& v)
  { do_work(v.value(), v.next()); }

  // actual method
  template <class var>
  void do_work(const char* s, var& v);
};


template <class var>
void qprintf_t::do_work(const char *s, var& v)
{
  while (*s)
  {
    if (*s=='%' && *(++s)!='%')
    {
      std::cout << v.value() ;
      do_work(*s?++s: s, v.next());
      return;
    }
    std::cout << *s++;
  }
  throw int(); // too many arguments
}

// specialization when we run out of arguments
template <>
void qprintf_t::do_work<variodic0>(const char *s, variodic0& v)
{
  while (*s)
  {
    if (*s=='%' && *(++s)!='%')
      throw int(); // too few arguments
    std::cout << *s++;
  }
}

variodic_function<qprintf_t> qprintf;


=============================================
arithmetic_h:

#ifndef _CHECK_ARITHMETIC_H__
#define _CHECK_ARITHMETIC_H__

static const int bool_code=0;
static const int char_code=1;
static const int uchar_code=1;
static const int short_code=2;
static const int ushort_code=2;
static const int int_code=3;
static const int uint_code=3;
static const int long_code=4;
static const int ulong_code=4;
static const int float_code=5;
static const int double_code=6;
static const int other_code=7;

template <class T> struct promotion_classify { static const int type_code=other_code; };
template <class T> struct promotion_classify< const T> : public promotion_classify<T> {};
template <> struct promotion_classify <bool> { static const int type_code=bool_code; };
template <> struct promotion_classify <char> { static const int type_code=char_code; };
template <> struct promotion_classify <unsigned char> { static const int type_code=uchar_code; };
template <> struct promotion_classify <short> { static const int type_code=short_code; };
template <> struct promotion_classify <unsigned short> { static const int type_code=ushort_code; };
template <> struct promotion_classify <int> { static const int type_code=int_code; };
template <> struct promotion_classify <unsigned int> { static const int type_code=uint_code; };
template <> struct promotion_classify <long> { static const int type_code=long_code; };
template <> struct promotion_classify <unsigned long> { static const int type_code=ulong_code; };
template <> struct promotion_classify <float> { static const int type_code=float_code; };
template <> struct promotion_classify <double> { static const int type_code=double_code; };

// determine presidence
template <class T1,bool, bool> struct arithmetic_promote_check
{ typedef T1 result_type; };
template <class T1> struct arithmetic_promote_check<T1,true,false>
{ typedef int result_type; };
template <class T1> struct arithmetic_promote_check<T1,false,true>
{ typedef long int result_type; };

template <class T1> struct arithmetic_promote
{ typedef typename arithmetic_promote_check<T1,
    (promotion_classify<T1>::type_code<int_code),
    (promotion_classify<T1>::type_code==int_code)
   >::result_type result_type; };

template <class T1, class T2, bool> struct arithmetic_presidence_check
{ typedef typename arithmetic_promote<T1>::result_type result_type; };
template <class T1, class T2> struct arithmetic_presidence_check<T1,T2,false>
{ typedef typename arithmetic_promote<T2>::result_type result_type; };

template <class T1, class T2> struct arithmetic_presidence
{ typedef typename arithmetic_presidence_check< T1, T2,
    (promotion_classify<T1>::type_code>promotion_classify<T2>::type_code) >::result_type result_type;

#endif


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]