As we know:
<.
struct TestFunky
{
int val;
char chr;
TestFunky( int v, char c)
: val(v)
, chr(c)
{}
};
int funky( TestFunky& v)
{
return v.val;
}
char funky( TestFunky& v) // compiler will not accept this overload!!!
{
return v.chr;
}
TestFunky testy( 666, 'E');
int i = funky(testy); // but we want to write it this way
char c = funky(testy); // but we want to write it this way
.>
doesn't work!
I've read a lot of (pseudo) arguments why this fact is a must. (I
disagree with (almost) all of them (as far as I remember). Anyway: it
is like it is.)
But there is a solution that solves the task (even though I don't
know, where it might make sense...):
<.
struct TestFunky
{
int val;
char chr;
TestFunky( int v, char c)
: val(v)
, chr(c)
{}
};
class funky
{
TestFunky& tf_;
public:
funky(TestFunky& tf)
: tf_(tf)
{}
operator int()
{
return tf_.val;
}
operator char()
{
return tf_.chr;
}
};
TestFunky testy( 666, 'E');
// and so we do as we wanted to.
int i = funky(testy);
char c = funky(testy);
// .and it even doesn't need more CPU load! (If the compiler isn't too
dumb.)
.>
Of course
<.
funky(testy); // doesn't work
.>
but e. g.
<.
(char)funky(testy); // works fine
.>
As I mentioned before, I don't have a good cause for that trick - just
a kind of finger exercise. ;-)
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
o_O
It seems that the moderation software already took care of the issue. ;)
jaybus56 wrote:
[overloading on the returnvalue]
> I've read a lot of (pseudo) arguments why this fact is a must. (I
> disagree with (almost) all of them (as far as I remember). Anyway: it
> is like it is.)
Hmmm, the main issue is that you can choose to ignore the returnvalue,
leaving the compiler with no way to actually figure out which overload you
wanted.
That said, you _can_ have a function overloaded by the returntype and I
actually use that a lot. The simple solution is to have a template
parameter. Then, you you simply say it explicitly which overload you want:
int x = parse<int>(some_string);
float f = boost::lexical_cast<float>(another_string);
char* c = reinterpret_cast<char*>(42);
Okay, the last one is actually not an overloaded function, but I like the
similarity of the syntax.
> struct TestFunky
> {
> int val;
> char chr;
> TestFunky( int v, char c)
> : val(v)
> , chr(c)
> {}
> };
> class funky
> {
> TestFunky& tf_;
> public:
> funky(TestFunky& tf)
> : tf_(tf)
> {}
>
> operator int()
> {
> return tf_.val;
> }
>
> operator char()
> {
> return tf_.chr;
> }
> };
> TestFunky testy( 666, 'E');
> // and so we do as we wanted to.
> int i = funky(testy);
> char c = funky(testy);
A bit complicated for my taste...
> // .and it even doesn't need more CPU load! (If the compiler isn't too
> dumb.)
Really? You first need both the 666 and the 'E', which are IIUC the results
of the two function calls, so typically one of them is wasted. That's also
why I'd use a less complicated one:
int int_function();
char char_function();
struct proxy {
operator int() const {
return int_function();
}
operator char() const {
return char_function();
}
};
proxy function() {
return proxy();
}
Here, you only have a single proxy object that call one of the functions on
demand, depending on which conversion is requested.
> Of course
> <.
> funky(testy); // doesn't work
> .>
The problem here is that the actual function isn't called (my code) or both
are called (your code). In some cases I can actually live with that or a
runtime error.
Uli
--
Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
> > I've read a lot of (pseudo) arguments why this fact is a must. (I
> > disagree with (almost) all of them (as far as I remember). Anyway: it
> > is like it is.)
>
> Hmmm, the main issue is that you can choose to ignore the returnvalue,
> leaving the compiler with no way to actually figure out which overload you
> wanted.
And *then* it would be the right reason for the compiler to complain.
Still, I can imagine that explicit cast would be used to disambiguate
the call just like we already do for taking pointers to overloaded
functions.
The problem of ambiguous calls is very easy to solve (don't assume
everything will be ambiguous, only complain when it actually is) and
the bare *possibility* to have an ambiguous call is a weak excuse for
throwing out the baby with the bath water.
--
Maciej Sobczak * www.msobczak.com * www.inspirel.com
The C++ Database Access Library: soci.sourceforge.net
--
We often have abiguities, don't we? In that case we have to solve it
by specifying in detail the function we want to use. (A simple
"(int)funky( tf);" could do it.)
>
> That said, you _can_ have a function overloaded by the returntype and I
> actually use that a lot. The simple solution is to have a template
> parameter. Then, you you simply say it explicitly which overload you want:
>
> int x = parse<int>(some_string);
> float f = boost::lexical_cast<float>(another_string);
> char* c = reinterpret_cast<char*>(42);
Sure, but the precondition was: no "decoration"; the lvalue shall
select the correct funcktion.
...
>
> A bit complicated for my taste...
Well, it is complicated (and as I said, it was a finger exercise,
only).
>
> > // .and it even doesn't need more CPU load! (If the compiler isn't too
> > dumb.)
>
> Really? You first need both the 666 and the 'E', which are IIUC the results
> of the two function calls, so typically one of them is wasted.
No, I'm using a reference and not a temporary structure!
The constructor receives the *reference* exactly as a normal function
would -> no difference! The funky instance is only temporary thus a
smart optimizing compiler (not too dumb) will handle this well and not
copy the reference. The cast operator member function will then work
with the stack stored reference.
I saw this in the disassembly of the code from my VS2008. It looked
exactly like the disassembly when I had used a "decorated" normal
function. (VS2008 is obviously smart enough.)
> That's also
> why I'd use a less complicated one:
>
> int int_function();
> char char_function();
>
> struct proxy {
> operator int() const {
> return int_function();
> }
> operator char() const {
> return char_function();
> }
> };
>
> proxy function() {
> return proxy();
> }
>
> Here, you only have a single proxy object that call one of the functions on
> demand, depending on which conversion is requested.
Beside you didn't mention the reference parameter and the (probably)
necessary "inline" key word: Okay, this will work, too. But is this
really less complicated?
>
> > Of course
> > <.
> > funky(testy); // doesn't work
> > .>
>
> The problem here is that the actual function isn't called (my code) or both
> are called (your code). In some cases I can actually live with that or a
> runtime error.
>
I tried "funky(testy);" with VS2008 and got a compiler error (not a
call of both casts, which compiler did you use?). When I used
"(int)funky(testy);" instead (which looks exactly like the solution of
cases of abiguity in my first comment above) it worked as desired and
called the "operator int(...)" nothing else.
br
Juergen
--
Your idea is very interesting, just amazing in fact, I've expanded it
a bit, probably it will be useful for somebody. Using it, it is
possible to provide any number of overloading functions, for example
with the following way:
char f_char()
{std::cout<<"f_char()\n";return char();}
int f_int()
{std::cout<<"f_int()\n";return int();}
short f_short(int) //!! Function accepts parameter
{std::cout<<"f_short()\n";return short();}
char a = f(f_char); // prints "f_char()"
int b = f(f_char, f_int); // prints "f_int()"
char c = f(f_char, f_int); // prints "f_char()"
short d = f(f_char, boost::bind(f_short,5)); //!! Binders
allowed // prints "f_short()"
int e = f(f_int, f_short); //!! Function without binded parameter
allowed // prints "f_int()"
char f = f(f_char, f_char); //!! Cause compilation error because of
ambiguity
So, the implementation itself below (yeah, I know, this code is not
ideal :-) ):
#include <boost/mpl/list.hpp>
#include <boost/mpl/and.hpp>
#include <boost/mpl/bool.hpp>
#include <boost/mpl/pop_front.hpp>
#include <boost/mpl/front.hpp>
#include <boost/mpl/contains.hpp>
#include <boost/preprocessor/repetition.hpp>
#include <boost/type_traits/function_traits.hpp>
#include <boost/type_traits/remove_pointer.hpp>
#include <boost/type_traits/is_function.hpp>
#include <boost/type_traits/is_pointer.hpp>
// Maximum number of overload variants
#ifndef RETTYPE_OVERLOAD_MAX_COUNT
#define RETTYPE_OVERLOAD_MAX_COUNT 10
#endif
namespace{
// Just a static check used to verify that all types in the
overloads list are unique
namespace static_check{
template<bool>
struct check{inline check(){}};
template<>
struct check<false>;
};
#define STATIC_CHECK(expr,msg)
static_check::check<expr>(ERROR_##msg);
// Helper function used to check if all types in the sequence are
unique
template<class l>
struct is_unique : boost::mpl::and_<
boost::mpl::not_<
boost::mpl::contains<
typename boost::mpl::pop_front<l>::type,
typename boost::mpl::front<l>::type
>
>,
is_unique<
typename boost::mpl::pop_front<l>::type
>
>
{};
template<>
struct is_unique<boost::mpl::l_end> : boost::mpl::true_
{};
// Just a helper used to get result type both from binded functors
and functions
template<class is_function,class T>
struct get_result_type_impl
{
typedef typename boost::function_traits<typename
boost::remove_pointer<T>::type>::result_type result_type;
};
template<class T>
struct get_result_type_impl<boost::mpl::false_,T>
{
typedef typename T::result_type result_type;
};
template<class T>
struct get_result_type
{
typedef typename get_result_type_impl<
typename boost::mpl::and_<
boost::is_pointer<T>,
boost::is_function<typename boost::remove_pointer<T>::type>
>::type,
T
>::result_type result_type;
};
// Trick kernel, provides type cast function for each type in the
list
template<class type,class l>
struct proxy_impl : proxy_impl<
typename boost::mpl::front<l>::type,
typename boost::mpl::pop_front<l>::type>
{
typedef proxy_impl<
typename boost::mpl::front<l>::type,
typename boost::mpl::pop_front<l>::type> base;
proxy_impl(type t,const base& b)
:t_(t),base(b)
{}
operator typename get_result_type<type>::result_type()const
{
return t_();
}
private:
const type t_;
};
template<class type>
struct proxy_impl<type,boost::mpl::l_end>
{
proxy_impl(type t)
:t_(t)
{}
operator typename get_result_type<type>::result_type()const
{
return t_();
}
private:
const type t_;
};
template<class l>
struct proxy : proxy_impl<
typename boost::mpl::front<l>::type,
typename boost::mpl::pop_front<l>::type>
{
STATIC_CHECK(is_unique<l>::value,each_return_type_in_the_functions_list_must_be_unique)
typedef proxy_impl<
typename boost::mpl::front<l>::type,
typename boost::mpl::pop_front<l>::type
> base;
};
}
// Basic variant, only one type cast provided
template<class T0>
typename proxy<boost::mpl::list<T0> >::base f(T0 t0)
{
return proxy<boost::mpl::list<T0> >::base(t0);
}
// Generate code for possible overloads
#define RETTYPE_generate(z,n,unused) \
template<BOOST_PP_ENUM_PARAMS(n,class T)> \
typename proxy< \
boost::mpl::list<BOOST_PP_ENUM_PARAMS(n,T)> \
>::base \
f(BOOST_PP_ENUM_BINARY_PARAMS(n,T,t)) \
{ \
return typename proxy< \
boost::mpl::list<BOOST_PP_ENUM_PARAMS(n,T)> \
>::base(t0,f(BOOST_PP_ENUM_SHIFTED_PARAMS(n,t))); \
}
BOOST_PP_REPEAT_FROM_TO(2,RETTYPE_OVERLOAD_MAX_COUNT,RETTYPE_generate,~)
#undef RETTYPE_generate
If somebody have anything to add concerning code quality or there is a
bug in this code, please, let me know.
----
Cheers,
Dmitry,
Mera Networks (http://meranetworks.com/)
--
> template<class l> struct is_unique
I think you can do this in linear time -- insert everything into an
mpl::set and chack that it has the same size as the list.
Martin
--
Quidquid latine scriptum est, altum videtur.
Hmmm... I've suspected there is another way, and I'm reinventing the
wheel with this code.
Thank you!
--