[C++-sig] Quick question about wrapping methods that have multiple versions

troy d. straszheim troy at resophonic.com
Wed Sep 9 18:44:05 CEST 2009


Nicolas Lelong wrote:
>> You may disambiguate by using a cast inside .def(), such as
>>
>>   .def("getA", (A*(B::*)())B::getA);
>>
>>  Whether that's actually more readable is arguable, however.
> 
> IMHO, this is quite dangerous as the explicit cast prevents the compiler 
> to give you a proper error if the signature of B::getA changes at some 
> point in time. The original approach ( A* (B::*B_getA1)() = &B::getA; ) 
> requires extra typing but provides a safety net for refactoring B::getA.
> 

This is an interesting subtlety.  I'd be advocating the C-style cast for 
its conciseness; as it turns out (with gcc 4.3 at least) I can only get 
dangerous behavior if I cast a function which is *not* overloaded.  For 
overloaded functions, if you cast to a function type which is not in the 
overload set, the C-style cast fails (as we would like it to), since 
there is incomplete type information.  So in the situation where you're 
using c-style casts to resolve overloads, and later in the wrapped class 
an overload set is reduced to one function, you have undefined behavior.
(example below... sanity-check, please?  Maybe there are other compilers 
on which this C-style casting is more dangerous than recent gcc.)

I was curious about this, as I have a def() syntax working that was 
suggested at boostcon:

struct C
{
   void f(float);
   void f(bool);
};

class_<C>("C")
   .def<int(float)>("f", f)
   .def<int(bool)>("f", f)
   ;

Wherein the conversion is done via some boost::function_types trickery. 
  I'll have to go back and check if it handles this case.


Here is the example:

// can you make the C-style casts of f, which select
// particular overloads, result in undefined behavior?

#include <iostream>

void f(int i)   { std::cout << "f(int) called with " << i <<" \n"; }
void f(double d) { std::cout << "f(double) called with" << d << "\n"; }

//
//  You can do this, cast a function to an int, but only if
//  the function is not overloaded:
//
void g(const char* thing) { std::cout << "g(const char*) called with "
                                       << thing << "\n"; }
int g_casted = (int) g; // this works but we wish it didnt

// Here the alias won't work: catches the error as we want, good
// void (*gptr)(float) = &g;

// Doesn't work:  overloaded function with no textual type information
// int f_casted = (int) f;

// this works, but is a degenerate case imho, first you cast to a
// function type, so that you have a type, then you can munge
// it into an int
int f_casted = (int) (void(*)(double)) f;

template <typename F>
void def(const char* name, const F& f)
{
   std::cout << name << "\t --> ";
   f(true);
}

// void (*f_bool_alias)(bool) = &f;
void (*f_int_alias)(int) = &f;
// void (*f_float_alias)(float) = &f;
void (*f_double_alias)(double) = &f;

int main(int, char**)
{
   // def(" via cast f(bool)", (void(*)(bool))f);   // wont compile
   def(" via cast f(int)", (void(*)(int))f);

   // def(" via cast f(float)", (void(*)(float))f); // wont compile
   def(" via cast f(double)", (void(*)(double))f);

   // def("via alias f(bool)", f_bool_alias);       // wont compile
   def("via alias f(int)", f_int_alias);
   // def("via alias f(float)", f_float_alias);     // wont compile
   def("via alias f(double)", f_double_alias);

   // def("via cast from g", (void(*)(float)) g);   // kaboom

}


More information about the Cplusplus-sig mailing list