Narinder said:
Narinder wrote:
Narinder wrote:
At the moment, I don't see why.
That is indeed curious.
However on my issue, I think I am expecting something from C++ ...
when I obviously should know better, "C++ (101): C++ doesn't
dispatch on the return type".
That, I think, is not the entire picture. There is clause [14.8.2..3]
that deals with deducing template arguments for templated conversion
operators. Here, the result type desired by the conversion is taken
into account. Howerver, reading that clause, I find it hard to tell
whether the deduction should fail because of ambiguity, whether the
const version should be instantiated, or whether the non-const
version should be chosen.
My question stems from wanting to pass parameters to a function
via an intermediary which holds references to the values to be
passed. And this would be done at runtime.
Given :
void f( double x);
I want to be able to do:
const double x=3.142;
intermediary v = x; // so v will hold a
std::tr1::reference_wrapper<const double>
f(v); // I suspect gcc will complain because v is
unable to give up its const ref
// but as we have seen gcc calls operator
double&()
Hm, the following compiles:
#include <iostream>
void foo ( double arg ) {
std::cout << arg << "\n";
}
template < typename T >
class reference_wrapper {
T* the_ptr;
public:
typedef T type;
typedef T& reference;
reference_wrapper ( reference t_ref )
: the_ptr ( &t_ref )
{}
operator reference ( void ) {
return ( *the_ptr );
}
reference get ( void ) {
return ( *the_ptr );
}
};
int main ( void ) {
double const pi = 3.14159;
reference_wrapper< double const > x = pi;
foo( x );
}
My way around this will be to have the user provide a precise
boost typelist embodying the function signature:
boost::mpl::vector<double>
and use this to dispatch correctly.
Huh?
Best,
Kai-Uwe Bux
Consider the following functions:
f_ref(double&) // may be a member function that keeps
the rw ref
f_const_ref(const double&) // maybe a memeber function that keeps
the ro ref
f_val(double) // expects the value byval
you have one 'variant'
variant v = 3.142;
double x=3.142;
variant v2 = x;
const double xx=3.142
variant v3 = 3.142
I want to deliver (since it doesn't have to be done by casting) v2to
f_ref, but not v & v3
I want to deliver v2 & v3 to f_const_ref but not v
and i want to be able to deliver all of them to f_val
Moreover my variants will be initialised at runtime.
I have sketched (but compiles and executes ) what I think is a
solution.
(I have used boost::mpl::vector to be able to generalise to more than
one parameter)
-------------------------------
#include<iostream>
#include<boost/ref.hpp>
#include<boost/variant.hpp>
#include<boost/mpl/vector.hpp>
#include<boost/mpl/at.hpp>
using namespace std;
double pi = 3.142;
typedef boost::reference_wrapper<double> ref;
typedef boost::reference_wrapper<const double> const_ref;
struct klass
{
klass(double & x):innerVariant(boost::ref(x)){}
klass(const double & x):innerVariant(boost::cref(x)){}
template<class T>
operator T& ()
{
cout << " ... casting to double& ";
return boost::get<ref>(innerVariant);
}
template<class T>
operator const T&()
{
cout << " ... casting to const double& ";
if(boost::get<ref>(&innerVariant))
{
return boost::get<ref>(innerVariant);
}
return boost::get<const_ref>(innerVariant);
}
const double &get_by_val()
{
return (const double&)(*this);
}
private:
boost::variant<ref,const_ref > innerVariant;
};
template<class T,class U>
struct innerCast
{
static const T & get(U &k)
{
return k.get_by_val();
}
};
template<class T,class U>
struct innerCast<T&,U>
{
static T & get(U &k)
{
return k;
}
};
template<class T,class U>
struct innerCast<const T&,U>
{
static const T & get(U &k)
{
return k;
}
};
template<class T>
struct cast
{
typedef typename boost::mpl::at_c<typename T:
aram_type,0>::type
ret_type;
static ret_type get(klass &k)
{
return innerCast<ret_type,klass>::get(k);
}
};
/////////////////////////////////////////
// Below is what would be the user code
/////////////////////////////////////////
struct f
{
typedef boost::mpl::vector<double&>::type param_type;
static void call(double &x)
{
cout << " double &x\n";
}
};
struct fc
{
typedef boost::mpl::vector<const double&> param_type;
static void call(const double &x)
{
cout << " const double &x\n";
}
};
struct ft
{
typedef boost::mpl::vector<double> param_type;
static void call(double x)
{
cout << " double\n";
}
};
int main()
{
try
{
double x(3.142);
klass k_non_const(x);
klass k_const(3.142);
f::call( cast<f>::get(k_non_const) );
fc::call( cast<fc>::get(k_non_const));
ft::call( cast<ft>::get(k_non_const));
fc::call( cast<fc>::get(k_const));
ft::call( cast<ft>::get(k_const)); // this one *NO LONGER*
throws with gcc
}
catch(const std::exception &err)
{
cout << err.what() << "\n";
}
}
---------------------------------------------
Maybe some of the class names have alot of scope to be more
meaningful
Here is an alternative idea (hopefully, I understood your aim):
#include <iostream>
struct oops {};
template < typename T >
class const_reference {
T const * ptr;
public:
const_reference ( T const & r )
: ptr ( &r )
{}
T const & get ( void ) {
return ( *ptr );
}
operator T const & ( void ) {
return ( *ptr );
}
};
template < typename T >
class reference {
T * ptr;
public:
reference ( T & r )
: ptr ( &r )
{}
T & get ( void ) {
return ( *ptr );
}
operator T & ( void ) {
return ( *ptr );
}
};
template < typename T >
class wrapper {
private:
T * the_ptr;
T const * the_c_ptr;
public:
wrapper ( T & ref )
: the_ptr ( &ref )
, the_c_ptr ()
{}
wrapper ( T const & cref )
: the_ptr ()
, the_c_ptr ( &cref )
{}
operator reference<T> ( void ) {
std::cout << "non-const\n";
if ( the_ptr ) {
return ( *the_ptr );
}
throw ( oops() );
}
operator const_reference<T> ( void ) {
std::cout << "const\n";
if ( the_ptr ) {
return ( *the_ptr );
}
return ( *the_c_ptr );
}
operator T ( void ) {
if ( the_ptr ) {
return ( *the_ptr );
}
return ( *the_c_ptr );
}
};
double const pi = 3.14159;
double e = 2.71828;
typedef wrapper< double > klass;
void foo_ref ( reference<double> ) {
std::cout << "reference\n";
}
void foo_cref ( const_reference<double> ) {
std::cout << "const reference\n";
}
void foo_val ( double ) {
std::cout << "value\n";
}
int main ( void ) {
klass rw ( e );
klass ro ( pi );
foo_val( rw );
foo_cref( rw );
foo_ref( rw );
foo_val( ro );
foo_cref( ro );
foo_ref( ro );
}
Best,
Kai-Uwe Bux
In my case the functions foo_ref, foo_cref, foo_val will be given (by
the client) and may ( and most probably will) be compiled without any
knowledge of the types reference and const_reference. So I would need
it to work with :
--------------------------
void foo_ref ( double & ) {
std::cout << "reference\n";
}
void foo_cref ( const double & ) {
std::cout << "const reference\n";
}
void foo_val ( double ) {
std::cout << "value\n";}
--------------------------
(In fact to be even more precise, in my real scenario they will be
class constructors.)
When I compile your code with above modifications I get the
compilation error:
-------------------------------------------------
main/main.cpp: In function 'int main()':
main/main.cpp:93:15: error: invalid initialization of reference of
type 'double&
' from expression of type 'klass'
main/main.cpp:72:6: error: in passing argument 1 of 'void
foo_ref(double&)'
main/main.cpp:96:15: error: invalid initialization of reference of
type 'double&
' from expression of type 'klass'
main/main.cpp:72:6: error: in passing argument 1 of 'void
foo_ref(double&)'
..also if I make the following amendments :
....
struct someClass
{
someClass(const double &x_):x(x_){}
void show()const{std::cout << "x=" <<x <<"\n";}
const double &x;
};
double const pi = 3.14159;
double e = 2.71828;
klass rw ( e );
klass ro ( pi );
foo_val( rw );
foo_cref( rw );
// foo_ref( rw );
foo_val( ro );
foo_cref( ro );
// foo_ref( ro );
someClass s(rw);
s.show();
e=0.0;
s.show();
}
It doesn't work as expected, specifically the last s.show() display 0.
Hm, what about:
#include <iostream>
struct oops {};
template < typename T >
class const_reference {
T const * ptr;
public:
const_reference ( T const & r )
: ptr ( &r )
{}
T const & get ( void ) {
return ( *ptr );
}
operator T const & ( void ) {
return ( *ptr );
}
};
template < typename T >
class reference {
T * ptr;
public:
reference ( T & r )
: ptr ( &r )
{}
T & get ( void ) {
return ( *ptr );
}
operator T & ( void ) {
return ( *ptr );
}
};
template < typename T >
class wrapper {
private:
T * the_ptr;
T const * the_c_ptr;
public:
wrapper ( T & ref )
: the_ptr ( &ref )
, the_c_ptr ()
{}
wrapper ( T const & cref )
: the_ptr ()
, the_c_ptr ( &cref )
{}
operator reference<T> ( void ) {
std::cout << "convert to non-const reference\n";
if ( the_ptr ) {
return ( *the_ptr );
}
throw ( oops() );
}
operator const_reference<T> ( void ) {
std::cout << "convert to const reference\n";
if ( the_ptr ) {
return ( *the_ptr );
}
return ( *the_c_ptr );
}
operator T ( void ) {
std::cout << "convert to value\n";
if ( the_ptr ) {
return ( *the_ptr );
}
return ( *the_c_ptr );
}
};
template < typename T >
T & convert_to_ref ( reference<T> r ) {
return ( r );
}
template < typename T >
T const & convert_to_cref ( const_reference<T> r ) {
return ( r );
}
template < typename T >
struct call_helper_ref {
void (&f) ( T & );
call_helper_ref ( void (&fct) ( T & ) )
: f ( fct )
{}
void operator() ( reference<T> arg ) const {
f( arg );
}
};
template < typename T >
struct call_helper_cref {
void (&f) ( T const & );
call_helper_cref ( void (&fct) ( T const & ) )
: f ( fct )
{}
void operator() ( const_reference<T> arg ) const {
f( arg );
}
};
template < typename T >
struct call_helper_value {
void (&f) ( T );
call_helper_value ( void (&fct) ( T ) )
: f ( fct )
{}
void operator() ( T arg ) const {
f( arg );
}
};
template < typename T >
call_helper_ref< T > call ( void (&f) ( T & ) ) {
return ( call_helper_ref<T>(f) );
}
template < typename T >
call_helper_cref< T > call ( void (&f) ( T const & ) ) {
return ( call_helper_cref<T>(f) );
}
template < typename T >
call_helper_value< T > call ( void (&f) ( T ) ) {
return ( call_helper_value<T>(f) );
}
template < typename C, typename T >
struct is_constructible {
struct yes_type { char d; };
struct no_type { yes_type a; yes_type b; };
static
T & ref ( void );
static
T const & cref ( void );
static
yes_type check ( C );
static
no_type check ( ... );
static
bool const from_ref =
sizeof( check( ref() ) ) == sizeof( yes_type );
static
bool const from_cref =
sizeof( check( cref() ) ) == sizeof( yes_type );
};
template < typename C, typename T,
bool from_non_const = is_constructible<C,T>::from_ref >
struct construct_helper;
template < typename C, typename T >
struct construct_helper<C,T,true> {
static
C eval ( wrapper<T> w ) {
return ( C( convert_to_ref<T>( w ) ) );
}
};
template < typename C, typename T >
struct construct_helper<C,T,false> {
static
C eval ( wrapper<T> w ) {
return ( C( convert_to_cref<T>( w ) ) );
}
};
template < typename C, typename T >
C construct_from_wrapper ( wrapper<T> w ) {
return ( construct_helper<C,T>::eval( w ) );
}
// client code
// ===========
double const pi = 3.14159;
double e = 2.71828;
typedef wrapper< double > klass;
void foo_ref ( double & ) {
std::cout << "reference\n";
}
void foo_cref ( double const & ) {
std::cout << "const reference\n";
}
void foo_val ( double ) {
std::cout << "value\n";
}
struct show {
double & the_ref;
show ( double & r )
: the_ref ( r )
{}
};
int main ( void ) {
klass rw ( e );
klass ro ( pi );
call(foo_val)( rw );
call(foo_cref)( rw );
call(foo_ref)( rw );
call(foo_val)( ro );
call(foo_cref)( ro );
//call(foo_ref)( ro );
show e_show = construct_from_wrapper<show>( rw );
std::cout << e_show.the_ref << "\n";
e = 2;
std::cout << e_show.the_ref << "\n";
e_show.the_ref = 1;
std::cout << e << "\n";
}
This does a gratuitous copy-construction. Maybe in C++0X, one can use move
or something.
Best,
Kai-Uwe Bux
Hi,
You're probably going to think I keep changing the goals posts
I assure you I am not.
The best way to illustrate my objective is to paste a degenerate
working demonstration of a my solution. It's degenerate in that it
still doesn't deal with a variable number of parameters. There are 3
source files. factory.h, main.cpp & pvariant.h . I do now have a
complete solution to my original objective and am more than happy to
forward that to you.
I will now copy and paste the code from you last post and play with
it.
Best Regards
Narinder
factory.h
------------------------------------
#include<typeinfo>
#include<vector>
#include"pvariant.h"
using namespace std;
// Our types
struct abstract
{
virtual double foo()const=0;
virtual ~abstract(){}
};
struct concrete: public abstract
{
virtual double foo()const
{
cout << "I am concrete1\n\n";
return 1.0;
};
};
struct concrete2 : public abstract
{
concrete2(){}
operator double(){return 1.0;}
virtual double foo()const
{
cout << "I am concrete2\n\n";
return 2.0;
};
};
// Our Types to be created by the factory
struct klass1
{
typedef boost::mpl::vector<double&,std::string&> signature;
klass1(double&x,std::string &s)
{
cout << "Constructed klass1\n";
s = "klass1 killed me ! \n";
}
void identify()const
{
cout << "I am klass1\n\n";
}
};
struct klass2
{
typedef boost::mpl::vector<const std::string&,abstract&>
signature;
klass2(const std::string &s,abstract& abs):theInterface(abs)
{
cout << "Constructed klass2\n";
}
void identify()const
{
cout << "I am klass2\n";
cout << "Calling foo in abstract ...";
theInterface.foo();
}
abstract &theInterface;
};
-------------------------------------------
************************************
main.cpp
------------------------------------------
#include<vector>
#include<iostream>
#include<string>
#include"factory.h"
typedef boost::mpl::vector<
double,
bool,
std::string,
char,
std::vector said:
::type variant_type_list;
typedef papa::Variant<variant_type_list> variant ;
template<class T>
T * create(variant p0,variant p1)
{
typedef typename boost::mpl::at_c<typename T::signature::type,
0>::type type_0;
typedef typename boost::mpl::at_c<typename T::signature::type,
1>::type type_1;
return new T(p0.get<type_0>(),p1.get<type_1>());
}
int main()
{
try
{
std::string s("Hello");
double pi = 3.142;
klass1 * ptr1 = create<klass1>(pi,s );
ptr1->identify();
cout << "s is now : " << s << "\n";
concrete2 c2;
klass2 * ptr2 = create<klass2>(s,c2 );
ptr2->identify();
concrete c1;
klass2 * ptr2b = create<klass2>(s,c1 );
ptr2b->identify();
}
catch(const char *err)
{
cout << err << "\n";
}
catch(const std::exception &err)
{
cout << "Exception : "<< err.what() << endl;
}
char ch;
cin >> ch;
return 0;
}
------------------------------------------------------
************************************
pvariant.h
----------------------------------------------------
#ifndef PVARIANT_H_GUARD
#define PVARIANT_H_GUARD
//#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
//#define BOOST_MPL_LIMIT_VECTOR_SIZE 20
// STD INCLUDES
// TR1 INCLUDES
#include<boost/tr1/type_traits.hpp>
#ifdef _MSC_VER
#include<boost/ref.hpp>
#else
#include<boost/tr1/functional.hpp>
#endif
// BOOST INCLUDES
#include<boost/variant.hpp>
// mpl
#include<boost/mpl/transform.hpp>
#include<boost/mpl/vector.hpp>
#include<boost/mpl/size.hpp>
#include<boost/mpl/filter_view.hpp>
#include<boost/mpl/contains.hpp>
#include<boost/mpl/transform_view.hpp>
#include<boost/mpl/insert_range.hpp>
#include<boost/mpl/assert.hpp>
#include<boost/mpl/at.hpp>
namespace papa
{
#ifdef _MSC_VER
namespace ref_wrapper_ns = boost;
#else
namespace ref_wrapper_ns = std::tr1;
#endif
template<class T>
ref_wrapper_ns::reference_wrapper<T> ref( T&t)
{
return ref_wrapper_ns::ref(t);
}
template<class T>
ref_wrapper_ns::reference_wrapper<const T> cref( const T&t)
{
return ref_wrapper_ns::cref(t);
}
namespace private_impl
{
template<class T>
struct add_ref_wrapper
{
typedef papa::ref_wrapper_ns::reference_wrapper<T> type;
};
template<class T>
struct add_const_ref_wrapper
{
typedef papa::ref_wrapper_ns::reference_wrapper<const T>
type;
};
template<int i>
struct is_1
{
static const bool value = false;
};
template<>
struct is_1<1>
{
static const bool value = true;
};
template<class seq1,class seq2>
struct concat
{
typedef typename boost::mpl::vector0<>::type
empty_vector_type;
typedef typename
boost::mpl::insert_range<empty_vector_type, typename
boost::mpl::end<empty_vector_type>::type, seq1>::type asvector;
typedef typename boost::mpl::insert_range<asvector,
typename boost::mpl::end<asvector>::type, seq2 >::type type;
};
}
template<class raw_variant_type_list>
class Variant
{
// The reason we are embedding two variants (const and non_const)
inside
// innerVariant instead of all the actual types themselves is to
overcome
// the default size limit of boost::mpl::vector
struct empty_type
{
empty_type(){}
};
typedef typename
boost::mpl::transform<raw_variant_type_list,typename
std::tr1::remove_cv<boost::mpl::_1>::type >::type
naked_variant_type_list;
typedef typename boost::mpl::transform<naked_variant_type_list,
private_impl::add_ref_wrapper<boost::mpl::_1> >::type
ref_variant_type_list;
typedef typename boost::mpl::transform<naked_variant_type_list,
private_impl::add_const_ref_wrapper<boost::mpl::_1> >::type
const_ref_variant_type_list;
typedef typename
boost::make_variant_over<ref_variant_type_list>::type ref_variant;
typedef typename
boost::make_variant_over<const_ref_variant_type_list>::type
const_ref_variant;
typedef typename
boost::mpl::vector<ref_variant,const_ref_variant>::type
variant_type_list ;
typedef typename
private_impl::concat<boost::mpl::vector<empty_type> ,variant_type_list>::type
variant_list_with_empty;
typedef typename
boost::make_variant_over<variant_list_with_empty>::type variant;
template<class T>
struct is_recognised_type
{
static const bool value =
boost::mpl::contains<naked_variant_type_list,T>::value ||
boost::mpl::contains<ref_variant_type_list,T>::value ||
boost::mpl::contains<const_ref_variant_type_list,T>::value ;
};
public:
Variant(){}
// RECOGNISED ONES
template<class T>
Variant(const T&t, const typename boost::enable_if_c<
boost::mpl::contains<naked_variant_type_list,T>::value,T>::type * ptr
=0)
:innerVariant(const_ref_variant(papa::cref<T>(t))){}
template<class T>
Variant(T&t, const typename boost::enable_if_c<
boost::mpl::contains<naked_variant_type_list,T>::value,T>::type * ptr
=0)
:innerVariant(ref_variant(papa::ref<T>(t))){}
template<class T>
Variant(const T&t, const typename boost::enable_if_c<
boost::mpl::contains<ref_variant_type_list,T>::value,T>::type * ptr
=0)
:innerVariant(ref_variant(t)){}
template<class T>
Variant(const T&t, const typename boost::enable_if_c<
boost::mpl::contains<const_ref_variant_type_list,T>::value,T>::type *
ptr =0)
:innerVariant(const_ref_variant(t)){}
// ONES THAT REQUIRE CASTING
// Conversions to interfaces
template<class T>
Variant(const T&t,
const typename
boost::disable_if_c<is_recognised_type<T>::value,T >::type * ptr =0,
const typename boost::disable_if_c<
boost::mpl::empty<
typename boost::mpl::filter_view<
::type>::value>::type * pt =0)
{
typedef typename
boost::mpl::filter_view said:
BOOST_MPL_ASSERT_MSG(private_impl::is_1 said:
::value, CONSTRUCTOR_OVERLOAD_RESOLUTION_WAS_AMBIGUOUS, (T,
possibilities ) );
innerVariant = const_ref_variant(papa::cref<typename
boost::mpl::at_c<possibilities,0>::type>(t));
}
template<class T>
Variant(T&t,
const typename
boost::disable_if_c<is_recognised_type<T>::value,T >::type * ptr =0,
const typename boost::disable_if_c<
boost::mpl::empty<
typename boost::mpl::filter_view<
::type>::value>::type * pt =0)
{
typedef typename
boost::mpl::filter_view said:
BOOST_MPL_ASSERT_MSG(private_impl::is_1 said:
::value, CONSTRUCTOR_OVERLOAD_RESOLUTION_WAS_AMBIGUOUS, (T,
possibilities ) );
innerVariant = ref_variant(papa::ref<typename
boost::mpl::at_c<possibilities,0>::type>(t));
}
// Conversions when we can
template<class T>
Variant(const T&t,
const typename
boost::disable_if_c<is_recognised_type<T>::value,T >::type * ptr =0,
const typename boost::disable_if_c<
boost::mpl::empty<
typename boost::mpl::filter_view<
::type>::value>::type * pt =0,
const typename boost::enable_if_c<
boost::mpl::empty<
typename boost::mpl::filter_view<
::type>::value>::type * pt2 =0)
{
typedef typename
boost::mpl::filter_view said:
BOOST_MPL_ASSERT_MSG(private_impl::is_1 said:
::value, CONSTRUCTOR_OVERLOAD_RESOLUTION_WAS_AMBIGUOUS, (T,
possibilities ) );
innerVariant = const_ref_variant(papa::cref<typename
boost::mpl::at_c<possibilities,0>::type>(t));
}
///// Ok the what we're really here for
template<class T>
T get()
{
return innerCast<T>::get(*this);
}
private:
template<class T>
const T& get_by_val()
{
// The (const T& in the following is
// necessary for VS 7.1
return get_const_ref<T>();
}
template<class T>
struct innerCast
{
static const T & get(Variant &v)
{
return v.template get_by_val<T>();
}
};
template<class T>
struct innerCast<T&>
{
static T & get(Variant &v)
{
return v.template get_non_const_ref<T>();
}
};
template<class T>
struct innerCast<const T&>
{
static const T & get(Variant &v)
{
return v.template get_const_ref<T>();
}
};
template<class T>
T& get_non_const_ref()
{
typedef papa::ref_wrapper_ns::reference_wrapper<T> ref_T;
ref_variant
*theVariant(boost::get<ref_variant>(&innerVariant));
if(theVariant)
{
ref_T * theRef(boost::get<ref_T>(theVariant));
if(theRef)
{
return *theRef;
}
throw ("Wrong Type");
}
throw ("Cant cast to const");
}
template<class T>
const T& get_const_ref()
{
typedef ref_wrapper_ns::reference_wrapper<T> ref_T;
typedef ref_wrapper_ns::reference_wrapper<const T>
const_ref_T;
ref_variant
*theNonConstVariant(boost::get<ref_variant>(&innerVariant));
if(theNonConstVariant)
{
return get_non_const_ref<T>();
}
const_ref_variant
*theVariant(boost::get<const_ref_variant>(&innerVariant));
if(theVariant)
{
const_ref_T * theRef(boost::get<const_ref_T>(theVariant));
if(theRef)
{
return *theRef;
}
throw ("Wrong Type");
}
throw ("Cant cast to const");
}
variant innerVariant;
};
}
#endif // PVARIANT_H_GUARD
-------------------------------------------------