lundi 29 juin 2015

Add const when accessing member variable

I need a transparent wrapper around a data structure to add some properties. Easiest is something like this:

template<typename T>
struct Wrapper{
    T values;
}

Now I want to pass this to an accessor and keep constness. Base would be:

template<class T>
T& accVal(usigned idx, T* vals){ return vals[idx]; }
template<class T>
const T& accVal(usigned idx, const T* vals){ return vals[idx]; }

template<class T>
auto
acc(unsigned idx, T& data) -> decltype(accVal(idx, data.values))
{
    return accVal(idx, data.values);
}

//Example:
Wrapper<int*> intsWrapped;
cout << acc(1, intsWrapped);

This works only for non-pointers, say replace "T*" with a struct as the access to data.values discards the constness of data and I'd be able to manipulate it like:

void foo(const Wrapper<int*>& bar){ acc(1, bar) = 5; }

That is dangerous in my application.

So how can I preserve the constness? I tried something like this:

template< class T_Base, typename T_Mem >
struct GetConstCorrect
{
    template< typename T >
    struct AddConstVal: std::add_const<T>{};

    template< typename T >
    struct AddConstVal<T&>
    {
        using type = std::add_const_t<T> &;
    };

    template< typename T >
    struct AddConstVal<T*>
    {
        using type = std::add_const_t<T>*;
    };

    template< typename T >
    struct AddConstVal<T* const>
    {
        using type = std::add_const_t<T>* const;
    };

    template< typename T >
    struct AddConstVal<T*&>
    {
        using type = std::add_const_t<T>*&;
    };

    template< typename T >
    struct AddConstVal<T*const &>
    {
        using type = std::add_const_t<T>* const &;
    };

    using Base = T_Base;
    using Mem = T_Mem;

    static constexpr bool isConst = std::is_const<Base>::value;
    using type = std::conditional_t< isConst,
            typename AddConstVal<Mem>::type,
            Mem
            >;
};

template< class T_Base, typename T_Mem >
using GetConstCorrect_t = typename GetConstCorrect< T_Base, T_Mem >::type;

template< class T_Base, typename T_Mem >
GetConstCorrect_t< T_Base, T_Mem& >
getConstCorrect(T_Mem& mem)
{
    return const_cast<GetConstCorrect_t< T_Base, T_Mem& >>(mem);
}

And access data.values by getConstCorrect(data.values), but this still seems error prone. (E.g. a multipointer like int** would become intconst not int const**)

Is there a better way to achieve this?

Aucun commentaire:

Enregistrer un commentaire