[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