Templated Casting operators

N

Narinder

Hello,
Can someone explain what should be the correct behaviour of the
following code:
__________________________________
#include<iostream>
using namespace std;
struct klass
{
template<class T>
operator T&()
{
throw("I am NON-const version");
}
template<class T>
operator const T&()
{
throw("I am CONST version");
}
};

int main()
{
try
{
klass k;
double x = k;
}
catch(const char *err)
{
cout << err << endl;
}
}

__________________________________
Compiled with MSVC 2010, language extensions disabled I get the
output: "I am CONST version"
Compiled with gcc version 4.5.2 (Ubuntu/Linaro 4.5.2-8ubuntu4) with
command line 'g++ -pedantic -ansi' I get the output :"I am NON-
const
version"
Thanks in advance.
N
 
A

Alexander Bartolich

Narinder said:
[...]
Can someone explain what should be the correct behaviour of the
following code:

Two functions in the same scope that differ only by return type?
My guess is that the behaviour is implementation defined.
struct klass
{
template<class T>
operator T&()
{
throw("I am NON-const version");
}
template<class T>
operator const T&()
{
throw("I am CONST version");
}
};

The second signature is missing a trailing "const":

operator const T&() const
 
N

Narinder

Narinder said:
[...]
Can someone explain what should be the correct behaviour of the
following code:

Two functions in the same scope that differ only by return type?
My guess is that the behaviour is implementation defined.
struct klass
{
        template<class T>
        operator T&()
        {
                throw("I am NON-const version");
        }
        template<class T>
        operator const T&()
        {
                throw("I am CONST version");
        }
};

The second signature is missing a trailing "const":

          operator const T&() const

I think if the behaviour is 'supposed' to be implementation-defined,
then that is a problem, isn't it ?
Consider the following code:

-------------------------------------------------------------

#include<iostream>
using namespace std;

double pi =3.142;

struct klass
{

template<class T>
operator T&()
{
cout << "I am NON-const version\n";
return pi;
}

template<class T>
operator const T&()const
{
cout << "I am CONST version\n";
return pi;
}


};

template<class T>
T& show(T &t)
{
cout << "NON-CONST show\n";
return t;
}

template<class T>
const T& show(const T &t)
{
cout << "CONST show\n";
return t;
}

int main()
{

klass k;
show(k);
show(klass());

double x1 = k;
double x2 = klass();

}

-----------------------------------------------------

Output from msvc :
NON-CONST show
CONST show
I am CONST version
I am CONST version

Output from gcc
NON-CONST show
CONST show
I am NON-CONST version
I am NON-CONST version

To me the msvc appears 'correct' or at least self-consistent. Or
perhaps I am missing something.

Any thoughts ?
 
K

Kai-Uwe Bux

Narinder wrote:

[...]
Consider the following code:

-------------------------------------------------------------

#include<iostream>
using namespace std;

double pi =3.142;

struct klass
{

template<class T>
operator T&()
{
cout << "I am NON-const version\n";
return pi;
}

template<class T>
operator const T&()const
{
cout << "I am CONST version\n";
return pi;
}


};

template<class T>
T& show(T &t)
{
cout << "NON-CONST show\n";
return t;
}

template<class T>
const T& show(const T &t)
{
cout << "CONST show\n";
return t;
}

int main()
{

klass k;
show(k);
show(klass());

double x1 = k;
double x2 = klass();

}

-----------------------------------------------------

Output from msvc :
NON-CONST show
CONST show
I am CONST version
I am CONST version

Output from gcc
NON-CONST show
CONST show
I am NON-CONST version
I am NON-CONST version

To me the msvc appears 'correct' or at least self-consistent. Or
perhaps I am missing something.

Any thoughts ?

I think, gcc gets it right and lets you observe a language quirk: In this
version, the two conversion operators differ by constness. Thus, overload
resolution should be based on constness of the klass object being converted:

klass k; // declares a non-const klass object k
show(k); // should choose the T& version
show( klass() ); // should choose the T const & version because of 8.5.3/5
double x1 = k; // should choose the non-const member as k is non-const
double x2 = klass(); // should choose the non-const member as the
// temporary klass() is non-const

The last issue is an inconsistency in the language: though klass() is non-
const, the temporary cannot be used to initialize a non-const reference.


Best,

Kai-Uwe Bux
 
N

Narinder

Narinder wrote:

[...]




Consider the following code:

#include<iostream>
using namespace std;
double pi =3.142;
struct klass
{
template<class T>
operator T&()
{
cout << "I am NON-const version\n";
return pi;
}
template<class T>
operator const T&()const
{
cout << "I am CONST version\n";
return pi;
}

template<class T>
T& show(T &t)
{
cout << "NON-CONST show\n";
return t;
}
template<class T>
const T& show(const T &t)
{
cout << "CONST show\n";
return t;
}
int main()
{
klass k;
show(k);
show(klass());
double x1 = k;
double x2 = klass();


Output from msvc :
NON-CONST show
CONST show
I am CONST version
I am CONST version
Output from gcc
NON-CONST show
CONST show
I am NON-CONST version
I am NON-CONST version
To me the msvc appears 'correct' or at least self-consistent. Or
perhaps I am missing something.
Any thoughts ?

I think, gcc gets it right and lets you observe a language quirk: In this
version, the two conversion operators differ by constness. Thus, overload
resolution should be based on constness of the klass object being converted:

  klass k; // declares a non-const klass object k
  show(k); // should choose the T& version
  show( klass() ); // should choose the T const & version because of 8.5.3/5
  double x1 = k; // should choose the non-const member as k is non-const
  double x2 = klass(); // should choose the non-const member as the
                       // temporary klass() is non-const

The last issue is an inconsistency in the language: though klass() is non-
const, the temporary cannot be used to initialize a non-const reference.

Best,

Kai-Uwe Bux

Hi
Thanks for the reply.

I was under the impress that a temporary variable would be cast only
as a
const &, which would make sense.

I find the output of the following code :

--------------------------------------------------------------
#include<iostream>
using namespace std;

double pi = 3.142;
struct klass
{

       template<class T>
       operator T&()
       {
               cout << "I am NON-const version\n";
               return pi;
       }

       template<class T>
       operator const T&()
       {
               cout << "I am CONST version\n";
               return pi;
       }
};

int main()
{

       klass k;
       double &x = k;
       const double &const_x = k;

}

-------------------------------------------------------------


more 'intuitivley' correct with msvc.
I am NON-const version
I am CONST version


than gcc
I am NON-const version
I am NON-const version
 
I

Ian Collins

Hi
Thanks for the reply.

I was under the impress that a temporary variable would be cast only
as a
const&, which would make sense.

I find the output of the following code :

--------------------------------------------------------------
#include<iostream>
using namespace std;

double pi = 3.142;
struct klass
{

template<class T>
operator T&()
{
cout<< "I am NON-const version\n";
return pi;
}

template<class T>
operator const T&()
{
cout<< "I am CONST version\n";
return pi;
}
};

int main()
{

klass k;
double&x = k;
const double&const_x = k;

}

-------------------------------------------------------------


more 'intuitivley' correct with msvc.
I am NON-const version
I am CONST version


than gcc
I am NON-const version
I am NON-const version

I think gcc is correct because k is non-const.

With gcc 4.5.1, I get

I am NON-const version
I am CONST version

So there must have been a recent change.
 
N

Narinder

Narinder wrote:

[...]




Consider the following code:

#include<iostream>
using namespace std;
double pi =3.142;
struct klass
{
template<class T>
operator T&()
{
cout << "I am NON-const version\n";
return pi;
}
template<class T>
operator const T&()const
{
cout << "I am CONST version\n";
return pi;
}

template<class T>
T& show(T &t)
{
cout << "NON-CONST show\n";
return t;
}
template<class T>
const T& show(const T &t)
{
cout << "CONST show\n";
return t;
}
int main()
{
klass k;
show(k);
show(klass());
double x1 = k;
double x2 = klass();


Output from msvc :
NON-CONST show
CONST show
I am CONST version
I am CONST version
Output from gcc
NON-CONST show
CONST show
I am NON-CONST version
I am NON-CONST version
To me the msvc appears 'correct' or at least self-consistent. Or
perhaps I am missing something.
Any thoughts ?

I think, gcc gets it right and lets you observe a language quirk: In this
version, the two conversion operators differ by constness. Thus, overload
resolution should be based on constness of the klass object being converted:

  klass k; // declares a non-const klass object k
  show(k); // should choose the T& version
  show( klass() ); // should choose the T const & version because of 8.5.3/5
  double x1 = k; // should choose the non-const member as k is non-const
  double x2 = klass(); // should choose the non-const member as the
                       // temporary klass() is non-const

The last issue is an inconsistency in the language: though klass() is non-
const, the temporary cannot be used to initialize a non-const reference.

Best,

Kai-Uwe Bux

Hi,

Thanks for the reply,

For the following code:

---------------------------------

#include<iostream>
using namespace std;

double pi = 3.142;
struct klass
{

       template<class T>
       operator T&()
       {
               cout << "I am NON-const version\n";
               return pi;
       }

       template<class T>
       operator const T&()
       {
               cout << "I am CONST version\n";
               return pi;
       }
};

int main()
{

       klass k;
       double x = k;
       double x2 = klass();

}

---------------------------------

msvc =>
I am CONST version
I am CONST version

and gcc =>
I am NON-const version
I am NON-const version

Which one would you say adhere's to language correctness and which one
is
desired ?

N
 
N

Narinder

Narinder wrote:

[...]




Consider the following code:

#include<iostream>
using namespace std;
double pi =3.142;
struct klass
{
template<class T>
operator T&()
{
cout << "I am NON-const version\n";
return pi;
}
template<class T>
operator const T&()const
{
cout << "I am CONST version\n";
return pi;
}

template<class T>
T& show(T &t)
{
cout << "NON-CONST show\n";
return t;
}
template<class T>
const T& show(const T &t)
{
cout << "CONST show\n";
return t;
}
int main()
{
klass k;
show(k);
show(klass());
double x1 = k;
double x2 = klass();


Output from msvc :
NON-CONST show
CONST show
I am CONST version
I am CONST version
Output from gcc
NON-CONST show
CONST show
I am NON-CONST version
I am NON-CONST version
To me the msvc appears 'correct' or at least self-consistent. Or
perhaps I am missing something.
Any thoughts ?

I think, gcc gets it right and lets you observe a language quirk: In this
version, the two conversion operators differ by constness. Thus, overload
resolution should be based on constness of the klass object being converted:

  klass k; // declares a non-const klass object k
  show(k); // should choose the T& version
  show( klass() ); // should choose the T const & version because of 8.5.3/5
  double x1 = k; // should choose the non-const member as k is non-const
  double x2 = klass(); // should choose the non-const member as the
                       // temporary klass() is non-const

The last issue is an inconsistency in the language: though klass() is non-
const, the temporary cannot be used to initialize a non-const reference.

Best,

Kai-Uwe Bux

Hi,

Thanks for the reply,

For the following code:

---------------------------------

#include<iostream>
using namespace std;

double pi = 3.142;
struct klass
{

       template<class T>
       operator T&()
       {
               cout << "I am NON-const version\n";
               return pi;
       }

       template<class T>
       operator const T&()
       {
               cout << "I am CONST version\n";
               return pi;
       }
};

int main()
{

       klass k;
       double x = k;
       double x2 = klass();

}

---------------------------------

msvc =>
I am CONST version
I am CONST version

and gcc =>
I am NON-const version
I am NON-const version

Which one would you say adhere's to language correctness and which one
is
desired ?

N
 
N

Narinder

Narinder wrote:
Consider the following code:
-------------------------------------------------------------
#include<iostream>
using namespace std;
double pi =3.142;
struct klass
{
template<class T>
operator T&()
{
cout << "I am NON-const version\n";
return pi;
}
template<class T>
operator const T&()const
{
cout << "I am CONST version\n";
return pi;
}
};
template<class T>
T& show(T &t)
{
cout << "NON-CONST show\n";
return t;
}
template<class T>
const T& show(const T &t)
{
cout << "CONST show\n";
return t;
}
int main()
{
klass k;
show(k);
show(klass());
double x1 = k;
double x2 = klass();
}
-----------------------------------------------------
Output from msvc :
NON-CONST show
CONST show
I am CONST version
I am CONST version
Output from gcc
NON-CONST show
CONST show
I am NON-CONST version
I am NON-CONST version
To me the msvc appears 'correct' or at least self-consistent. Or
perhaps I am missing something.
Any thoughts ?
I think, gcc gets it right and lets you observe a language quirk: In this
version, the two conversion operators differ by constness. Thus, overload
resolution should be based on constness of the klass object being converted:
  klass k; // declares a non-const klass object k
  show(k); // should choose the T& version
  show( klass() ); // should choose the T const & version because of 8.5.3/5
  double x1 = k; // should choose the non-const member as k is non-const
  double x2 = klass(); // should choose the non-const member as the
                       // temporary klass() is non-const
The last issue is an inconsistency in the language: though klass() is non-
const, the temporary cannot be used to initialize a non-const reference..

Kai-Uwe Bux

Hi,

Thanks for the reply,

For the following code:

---------------------------------

#include<iostream>
using namespace std;

double pi = 3.142;
struct klass
{

       template<class T>
       operator T&()
       {
               cout << "I am NON-const version\n";
               return pi;
       }

       template<class T>
       operator const T&()
       {
               cout << "I am CONST version\n";
               return pi;
       }

};

int main()
{

       klass k;
       double x = k;
       double x2 = klass();

}

---------------------------------

msvc =>
I am CONST version
I am CONST version

and gcc =>
I am NON-const version
I am NON-const version

Which one would you say adhere's to language correctness and which one
is
desired ?

N

Hello,

To elaborate on this a little more, I'll  put the issue that I have in
a
context more closer to my actual scenario.
The following code compiles and executes fine under msvc but throws
under
gcc.  I think the behaviour under gcc is much less desirable. I am
curious
about the language's rationale behind this:

---------------------------------------------------
#include<iostream>
#include<boost/ref.hpp>
#include<boost/variant.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 << "Attempting  non-const cast .... ";
               return boost::get<ref >(innerVariant);
       }

       template<class T>
       operator const T&()
       {
               cout << "Attempting  const cast .... ";
               if(boost::get<ref >(&innerVariant))
               {
                       returnboost::get<ref >(innerVariant);
               }
               return boost::get<const_ref >(innerVariant);
       }

private:
       boost::variant<ref,const_ref >  innerVariant;
};

void f(double &x)
{
       cout << "double &x\n";
}
void fc(const double &x)
{
       cout << "const double &x\n";
}
void ft(double x)
{
       cout << "double\n";
}
int main()
{

       try
       {
       double x(3.142);
       klass k_non_const(x);
       klass k_const(3.142);

       f(k_non_const);
       fc(k_non_const);
       ft(k_non_const);

       fc(k_const);
       ft(k_const); // this one throws with gcc
       }
       catch(const std::exception &err)
       {
               cout << err.what() << "\n";
       }

}
 
K

Kai-Uwe Bux

Narinder said:
Narinder wrote:

[...]




Consider the following code:

#include<iostream>
using namespace std;
double pi =3.142;
struct klass
{
template<class T>
operator T&()
{
cout << "I am NON-const version\n";
return pi;
}
template<class T>
operator const T&()const
{
cout << "I am CONST version\n";
return pi;
}

template<class T>
T& show(T &t)
{
cout << "NON-CONST show\n";
return t;
}
template<class T>
const T& show(const T &t)
{
cout << "CONST show\n";
return t;
}
int main()
{
klass k;
show(k);
show(klass());
double x1 = k;
double x2 = klass();


Output from msvc :
NON-CONST show
CONST show
I am CONST version
I am CONST version
Output from gcc
NON-CONST show
CONST show
I am NON-CONST version
I am NON-CONST version
To me the msvc appears 'correct' or at least self-consistent. Or
perhaps I am missing something.
Any thoughts ?

I think, gcc gets it right and lets you observe a language quirk: In this
version, the two conversion operators differ by constness. Thus, overload
resolution should be based on constness of the klass object being
converted:

klass k; // declares a non-const klass object k
show(k); // should choose the T& version
show( klass() ); // should choose the T const & version because of
8.5.3/5 double x1 = k; // should choose the non-const member as k is
non-const double x2 = klass(); // should choose the non-const member as
the // temporary klass() is non-const

The last issue is an inconsistency in the language: though klass() is
non- const, the temporary cannot be used to initialize a non-const
reference.

Best,

Kai-Uwe Bux

Hi,

Thanks for the reply,

For the following code:

---------------------------------

#include<iostream>
using namespace std;

double pi = 3.142;
struct klass
{

template<class T>
operator T&()
{
cout << "I am NON-const version\n";
return pi;
}

template<class T>
operator const T&()
{
cout << "I am CONST version\n";
return pi;
}
};

int main()
{

klass k;
double x = k;
double x2 = klass();

}

---------------------------------

msvc =>
I am CONST version
I am CONST version

and gcc =>
I am NON-const version
I am NON-const version

Which one would you say adhere's to language correctness and which one
is
desired ?

I would expect a compilation error due to ambiguity. The code seems to be
just a template version of something like this:

class k {

double value;

public:

k ( double v )
: value ( v )
{}

operator double & ( void ) {
std::cout << "non-const\n";
return ( value );
}

operator double const & ( void ) {
std::cout << "const\n";
return ( value );
}

};

int main ( void ) {
k x ( 13.4 );
double a = x;
double const b = x;
}

Here, you get a compilation error. I wonder why you don't with templates:
even Comeau compiles the template version. From the test case

#include <iostream>

template < typename T >
struct oops;

class k {

double value;

public:

k ( double v )
: value ( v )
{}

template < typename T >
operator T & ( void ) {
std::cout << "non-const\n";
return ( value );
}

template < typename T >
operator T const & ( void ) {
oops<T>::value;
std::cout << "const\n";
return ( value );
}

};

int main ( void ) {
k x ( 13.4 );
double a = x;
double const b = x;
}

and the version, where oops<T>::value is placed into the non-const version,
one can see that Comeau (like gcc) instantiates only the operator T&
template. At the moment, I don't see why.


Best,

Kai-Uwe Bux
 
N

Narinder

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".

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&()


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.

Thanks for your all your help.

Best Regards
N
 
K

Kai-Uwe Bux

Narinder said:
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
 
N

Narinder

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.
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:

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) v2 to
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::param_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 :)


Best Regards
N
 
K

Kai-Uwe Bux

Narinder said:
Narinder said:
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.
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:

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) v2 to
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::param_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";
}

}

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
 
N

Narinder

Narinder said:
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) v2 to
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)

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::param_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&)'
 
N

Narinder

Narinder said:
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 functionthat 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) v2 to
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::param_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";
    }
}
Here is an alternative idea (hopefully, I understood your aim):
#include <iostream>
struct oops {};
template < typename T >
class const_reference {
  T const * ptr;

  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;

  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;

  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 );


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;
};

int main ( void ) {

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();
}
 
N

Narinder

Narinder said:
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 functionthat 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) v2 to
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::param_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";
    }
}
Here is an alternative idea (hopefully, I understood your aim):
#include <iostream>
struct oops {};
template < typename T >
class const_reference {
  T const * ptr;

  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;

  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;

  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 );


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;
};

int main ( void ) {
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();
}
 
K

Kai-Uwe Bux

Narinder wrote:

[...]
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";
}
--------------------------

Ok, if cannot fiddle with the signatures, I think you can still avoid
explicitly mentioning types in client code:

#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 >
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) );
}



// 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";
}


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 );
}


(In fact to be even more precise, in my real scenario they will be
class constructors.)
[...]

Now, that is more tricky as "constructors don't have names". I have to
ponder whether template magic can detect whether a class has a constructor
by value, reference, or const-reference.


Best,

Kai-Uwe Bux
 
K

Kai-Uwe Bux

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.
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;

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:

and use this to dispatch correctly.


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) v2 to
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)

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::param_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()
{

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;

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;

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;

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 );


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;
};

int main ( void ) {

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();
}

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
 
N

Narinder

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::param_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;
};
int main ( void ) {
  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:
::type possibilities;


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:
::type possibilities;


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:
::type possibilities;


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

-------------------------------------------------
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,050
Latest member
AngelS122

Latest Threads

Top