Boost logo

Boost :

From: Howard Hinnant (hinnant_at_[hidden])
Date: 2000-06-10 10:26:46


I can repeat the thought process that I went through:

I started with the following templated class:

template <typename T>
class A
{
public:
        A(param_type data);
        value_type return_by_value() const;
        reference return_by_reference();
        const_reference return_by_const_reference() const;
private:
        T data_;
};

I wanted to use call_traits so that no matter what T was, I could:

     1. Pass T into the class (param_type)
     2. Pass T out of the class by value (value_type)
     3. Pass T out of the class by reference (reference)
     4. Pass T out of the class by const reference (const_reference)

I set up a test-bed to do this and experimented with pods, references and
arrays. The following test-bed is not a pass-fail sort of thing. You
have to study the code to decide what is "right".

template <typename T>
class A
{
public:
        typedef typename call_traits<T>::value_type value_type;
        typedef typename call_traits<T>::reference reference;
        typedef typename call_traits<T>::const_reference const_reference;
        typedef typename call_traits<T>::param_type param_type;
        A(param_type data) : data_(data) {}
        value_type return_by_value() const {return data_;}
        reference return_by_reference() {return data_;}
        const_reference return_by_const_reference() const {return data_;}
private:
        T data_;
};

template <typename T, std::size_t sz>
class A<T[sz]>
{
public:
        typedef typename call_traits<T[sz]>::value_type value_type;
        typedef typename call_traits<T[sz]>::reference reference;
        typedef typename call_traits<T[sz]>::const_reference const_reference;
        typedef typename call_traits<T[sz]>::param_type param_type;
        A(param_type data) {std::memcpy(data_, data, sz*sizeof(T));}
        value_type return_by_value() const {return data_;}
        reference return_by_reference() {return data_;}
        const_reference return_by_const_reference() const {return data_;}
private:
        T data_[sz];
};

int main()
{
        {
                int i = 1;
                std::cout << "i = " << i << '\n';
                A<int> a1(1);
                int& i1 = a1.return_by_reference();
                i1 = 2;
                std::cout << "i = " << i << '\n';
                std::cout << "i1 = " << i1 << '\n';
                const int& i2 = a1.return_by_const_reference();
                std::cout << "i2 = " << i2 << '\n';
                int i3 = a1.return_by_value();
                std::cout << "i3 = " << i3 << '\n';
        }
        std::cout << '\n';
        {
                int i = 1;
                std::cout << "i = " << i << '\n';
                A<int&> a1(i);
                int& i1 = a1.return_by_reference();
                i1 = 2;
                std::cout << "i = " << i << '\n';
                std::cout << "i1 = " << i1 << '\n';
                const int& i2 = a1.return_by_const_reference();
                std::cout << "i2 = " << i2 << '\n';
                int i3 = a1.return_by_value();
                std::cout << "i3 = " << i3 << '\n';
        }
        std::cout << '\n';
        {
                int* i = reinterpret_cast<int*>(0x01);
                std::cout << "i = " << i << '\n';
                A<int*> a1(i);
                int*& i1 = a1.return_by_reference();
                i1 = reinterpret_cast<int*>(0x02);
                std::cout << "i = " << i << '\n';
                std::cout << "i1 = " << i1 << '\n';
                int*const& i2 = a1.return_by_const_reference();
                std::cout << "i2 = " << i2 << '\n';
                int* i3 = a1.return_by_value();
                std::cout << "i3 = " << i3 << '\n';
        }
        std::cout << '\n';
        {
                int* i = reinterpret_cast<int*>(0x01);
                std::cout << "i = " << i << '\n';
                A<int*&> a1(i);
                int*& i1 = a1.return_by_reference();
                i1 = reinterpret_cast<int*>(0x02);
                std::cout << "i = " << i << '\n';
                std::cout << "i1 = " << i1 << '\n';
                int*const& i2 = a1.return_by_const_reference();
                std::cout << "i2 = " << i2 << '\n';
                int* i3 = a1.return_by_value();
                std::cout << "i3 = " << i3 << '\n';
        }
        std::cout << '\n';
        {
                typedef char Char4[4];
                Char4 i = "abc";
                std::cout << "i = " << i << '\n';
                A<Char4> a1(i);
                Char4& i1 = a1.return_by_reference();
                std::strcpy(i1, "def");
                std::cout << "i = " << i << '\n';
                std::cout << "i1 = " << i1 << '\n';
                const Char4& i2 = a1.return_by_const_reference();
                std::cout << "i2 = " << i2 << '\n';
                Char4 i3;
                std::strcpy(i3, a1.return_by_value());
                std::cout << "i3 = " << i3 << '\n';
        }
        std::cout << '\n';

        {
                typedef char Char4[4];
                Char4 i = "abc";
                std::cout << "i = " << i << '\n';
                A<Char4&> a1(i);
                Char4& i1 = a1.return_by_reference();
                std::strcpy(i1, "def");
                std::cout << "i = " << i << '\n';
                std::cout << "i1 = " << i1 << '\n';
                const Char4& i2 = a1.return_by_const_reference();
                std::cout << "i2 = " << i2 << '\n';
        }
}

The output I'm expecting is:

i = 1
i = 1
i1 = 2
i2 = 2
i3 = 2

i = 1
i = 2
i1 = 2
i2 = 2
i3 = 2

i = 0x00000001
i = 0x00000001
i1 = 0x00000002
i2 = 0x00000002
i3 = 0x00000002

i = 0x00000001
i = 0x00000002
i1 = 0x00000002
i2 = 0x00000002
i3 = 0x00000002

i = abc
i = abc
i1 = def
i2 = def
i3 = def

i = abc
i = def
i1 = def
i2 = def

I had to make a specialization of A<T> for arrays because of A's
constructor. But otherwise it is meant to be the same as the primary
template. Fixing up call_traits for arrays to make this work is what led
me to the design I posted. Perhaps my assumptions on the use of
call_traits are not what other's intended.

It might be a good idea to come up with more examples of how one would
expect to make use of call_traits with array types.

-Howard

John Maddock wrote on 6/10/2000 7:41 AM
>Dave,
>
>>Can someone explain the existence of the following in call_traits.hpp, and
>>how such code came to be accepted into a release library?
>
>Good question, doesn't look good does it? Looks like we forgot to finish
>this - in fact looking closer it's not clear how the array specialisation
>should look (and most current compilers don't actually need the array
>specialisation anyway), the most recent version (of several) was suggested
>by Howard, but doesn't meet the constructability guarantees. Here are the
>options:
>
>Option 1: doesn't meet constructability guarentees, but ensures that
>value_type is assignable:
>
>template <typename T, std::size_t N>
>struct call_traits<T [N]>
>{
> typedef T* const value_type; // hh was typedef T
>value_type[N];
> typedef T (&reference)[N]; // hh was typedef T*& reference;
> typedef const T (&const_reference)[N]; // hh was typedef const T*&
>const_reference;
> typedef value_type param_type; // hh was typedef T* param_type;
>};
>
>Option 2: meets constructability guarentees but value_type is not
>assignable:
>
>template <typename T, std::size_t N>
>struct call_traits<T [N]>
>{
>private:
> typedef T array_type[N];
>public:
> typedef array_type& value_type;
> typedef value_type reference;
> typedef const array_type& const_reference;
> typedef value_type param_type;
>};
>
>Thoughts?
>
>- John.


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk