Member template function which takes member function pointer as non template parameter.  
Author Message
Butterflow





PostPosted: Visual C++ Language, Member template function which takes member function pointer as non template parameter. Top

I am looking into detail of a fastest C++ delegate posted on codeproject. ( http://www.hide-link.com/ ). It seems the suggested implementation is at least a hundred times faster than boost::function in the case of copying and calling member function.

However, as it is stated in the original article, there were found to be several issues when compiling the suggested code in VC71 and VC8, while the same code compiled and run fine in gcc 3.4.2 or Intel C++ 9.1.

I discovered the following two issues are mostly causing compile errors in VC71 and VC8.

1) templater parameter can not be passed correctly into member template function as a portion of the signature of member function pointer of a non-type template parameter in partial template class. (Preferred syntax, so called, did not work in VC71 and VC8 due to this problem)

 
template<class T> class delegate;

template<class R, class T1> class delegate<R (T1)>
{
public:
template<class U, R (U::*TMFn)(T1)>
static delegate from_member(U * obj_ptr)
{
return delegate();
}
};
struct foo
{
void bar(int) { }
};

int main()
{
foo f;
delegate<void (int)>::from_member<foo, &foo::bar>( &f );
}
 

The above code snippet compile failed in VC71 and VC8 with the following error message.

main.cpp(112) : error C2440: 'specialization' : cannot convert from 'void (__thiscall foo::* )(int)' to 'R (__thiscall foo::* const )(T1)'
Types pointed to are unrelated; conversion requires reinterpret_cast, C-style cast or function-style cast
main.cpp(112) : error C2973: 'fd::delegate::from_member' : invalid template argument 'void (__thiscall foo::* )(int)'
with
[
T=void (int)
]
main.cpp(81) : see declaration of 'fd::delegate::from_member'
with
[
T=void (int)
]

2) When overloaded member function pointer is given for the member template function as non-type template parameter, an incorrect value is passed and can cause run-time error or compile-time error when using the passed value.

#include <cstdlib>
#include <iostream>


using namespace std;


template<class R, class T1>
class delegate1
{
struct fxn_table
{
R (*invoke)(void *, T1);
};


void * obj_;
fxn_table * table_ptr_;


template<class T, R (T::*TMFn)(T1)> struct method_stub
{
static R invoke(void * obj, T1 a1)
{
return ( static_cast<T *>( obj )->*TMFn )( a1 );
}
};


template<class T, R (T::*TMFn)(T1)> struct table
{
static fxn_table * get()
{
static fxn_table static_table =
{
&method_stub<T, TMFn>::invoke,
};
return &static_table;
}
};


public:

template<class T, R (T::*TMFn)(T1)>
static delegate1 from_method(T * obj)
{
delegate1 d;
d.table_ptr_ = table<T, TMFn>::get(); // (1)
d.obj_ = obj;
return d;
}


R operator ()(T1 a1) const
{
return ( *table_ptr_->invoke )( obj_, a1 );
}
};


struct foo
{
void bar(int) { std::cout << "foo::bar(int)" << std::endl; }


void foobar(int) { std::cout << "foo::foobar(int)" << std::endl; }
void foobar(char *) { std::cout << "foo::foobar(char *)" << std::endl; }
};


int main(int argc, char *argv[])
{
foo f;


delegate1<void, int> d1 = delegate1<void, int>::from_method<foo, &foo::bar>( &f );
d1( 1 );


delegate1<void, int> d2 = delegate1<void, int>::from_method<foo, &foo::foobar>( &f ); // (2) causes error C2975 at the line (1) above in VC8 and error C2563 in VC71
d2( 2 );


return 0;
}

The above code snippet is a variant of the original source code (which I am trying to improve features) but demonstrates that the following compile errors are being occurred in VC71 and VC8.

===== VC8 result =====
Compiling...
main.cpp
.\main.cpp(43) : error C2975: 'TMFn' : invalid template gument for 'delegate1<R,T1>::table', expected compile-time constant expression
with
[
R=void,
T1=int
]
.\main.cpp(26) : see declaration of 'TMFn'
.\main.cpp(69) : see reference to function template instantiation 'delegate1<R,T1> delegate1<R,T1>::from_method<foo,0>(T *)' being compiled
with
[
R=void,
T1=int,
T=foo
]


===== VC71 result =====
Compiling...
main.cpp
.\main.cpp(69) : error C2563: mismatch in formal parameter list

As clearly specified in the compile error messge above, non-type template parameter of member function poitner is passed as a value of '0' instead of the member function poitner to foo::foobar() when foobar() is an overloaded. Again, gcc 3.4.2 and Intel C++ 9.1 compiles and runs the same code without problem.

So I highly suspect that these are bugs of VC71 and VC8. right



Visual C++6  
 
 
einaros





PostPosted: Visual C++ Language, Member template function which takes member function pointer as non template parameter. Top

I'd expect both point 1 and 2 to compile, but clearly they don't. There have been a few other reportings of similar issues, so these may already have been submitted as bugs. You should go to http://connect.microsoft.com/VisualStudio and do a couple of searches, but probably report your findings as two separate matters given the same steps to reproduce as shown above.

 
 
Brian Kramer





PostPosted: Visual C++ Language, Member template function which takes member function pointer as non template parameter. Top

I agree: submit new bugs.  Sometimes it's difficult to find duplicate bugs when it comes to compilation, so I'd err on the side of leaving it to Microsoft to make that determination.

It looks like you got a handle on the issue, but let us know if you need help with a workaround.


 
 
Butterflow





PostPosted: Visual C++ Language, Member template function which takes member function pointer as non template parameter. Top

Hello einaros,

I've already submitted these to MS.

#1) https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx FeedbackID=251467

#2) https://connect.microsoft.com/VisualStudio/feedback/ViewFeedback.aspx FeedbackID=252195

Thanks.


 
 
Butterflow





PostPosted: Visual C++ Language, Member template function which takes member function pointer as non template parameter. Top

Hello Brian Kramer,

I had found a not-so-elegant workaround for these problems and I specified these workarounds in the bug report to MS of above links. It is somewhat incovenient and less intutive notation but works for VC71 and VC8. I just hope MS can incorporate fixes for these problem into VC8 SP1.

By the way,while struggling for workarounds, I've found a better (means, more portable but still standard-compliant) approach than the suggested implementation by the original author, which does not use member function pointer as non-type template parameter and this new implementation doesn't seem to suffer from the same issues described above.

template<class R, class T1> class delegate1
{
struct fxn_table
{
R (*invoke)(void *, void *, T1);
};
  void * obj_ptr_;
void * fn_ptr_;
fxn_table * table_ptr_;
  template<class UR, class U1, class U, class T> struct method_stub
{
inline static void init(void ** fn_pptr, void ** obj_pptr, UR (U::*mfn)(U1), T * obj_ptr)
{
typedef UR (U::*TMFn)(T1);
union { void * u_fn_ptr; TMFn u_mfn; };
u_mfn = mfn;
      *fn_pptr = u_fn_ptr;
*obj_pptr = obj_ptr;
}
    inline static R invoke(void * fn_ptr, void * obj_ptr, T1 a1)
{
typedef UR (U::*TMFn)(U1);
union { void * u_fn_ptr; TMFn u_mfn; };
u_fn_ptr = fn_ptr;
      return ( static_cast<T *>( obj_ptr )->*u_mfn )( a1 );
}
  };
  template<class UR, class U1, class U, class T> struct method_table
{
static fxn_table * get()
{
static fxn_table static_table =
{
&method_stub<UR, U1, U, T>::invoke,
};
return &static_table;
}
};
public:
  template<class U, class UR, class U1, class T> void bind(UR (U::*mfn)(U1), T * obj)
{
method_stub<UR, U1, U, T>::init( &fn_ptr_, &obj_ptr_, mfn, obj);
table_ptr_ = method_table<UR, U1, U, T>::get();
}
  inline R operator ()(T1 a1) const
{
return table_ptr_->invoke( fn_ptr_, obj_ptr_, a1 );
}
};
It seems a just little bit slower than Don's fastest delegate or Sergey's fast delegate, but 5~10 times faster for invocation and 5~50 times faster for copying than boost::function.
Thanks.