15

I have two structs:

  // ----- non-const -----
  struct arg_adapter
  {
      EArgType type;  // fmtA, fmtB, ...

      union
      {
        TypeA * valueA;
        TypeB * valueB;
        // ... more types
      }

      arg_adapter(TypeA & value) : type(fmtA), valueA(&value) {}
      arg_adapter(TypeB & value) : type(fmtB), valueB(&value) {}
      // ...
  }

  // ----- const version -----
  struct const_arg_adapter
  {
      EArgType type;  // fmtA, fmtB, ...

      union
      {
        TypeA const * valueA;
        TypeB const * valueB;
        // ... more types
      }

      arg_adapter(TypeA const & value) : type(fmtA), valueA(&value) {}
      arg_adapter(TypeB const & value) : type(fmtB), valueB(&value) {}
      // ...
  }

They are supposed to be used in methods such as:

  Convert(const_arg_adapter from, arg_adapter to)

There are multiple TypeX' (about 5, may become more), most of them primitive. This is to avoid maintaining different prototypes.

Now my question ;-)

Is there a way to make the const-ness a template parameter? My goal is to maintain only one struct, i.e.

template <Qualifier CONSTNESS>
struct arg_adapter_t
{
   ...
   CONSTNESS TypeA * valueA;
   ...
}
cHao
  • 84,970
  • 20
  • 145
  • 172
peterchen
  • 40,917
  • 20
  • 104
  • 186

3 Answers3

9

You can make it accept a metafunction and you can apply any transformation you like

template<template<typename> class F>
struct arg_adapter
{
    EArgType type;  // fmtA, fmtB, ...

    union
    {
      typename F<TypeA>::type * valueA;
      typename F<TypeB>::type * valueB;
      // ... more types
    };

    arg_adapter(typename F<TypeA>::type & value) : type(fmtA), valueA(&value) {}
    arg_adapter(typename F<TypeB>::type & value) : type(fmtB), valueB(&value) {}
    // ...
};

typename arg_adapter<boost::add_const> const_adapter;
typename arg_adapter<boost::mpl::identity> nonconst_adapter;

Or accept a metafunction class to get more flexibility (including the ability to make F have default arguments not known to your arg_adapter and such.

template<typename F>
struct arg_adapter
{
    EArgType type;  // fmtA, fmtB, ...

    union
    {
      typename apply<F, TypeA>::type * valueA;
      typename apply<F, TypeB>::type * valueB;
      // ... more types
    };

    arg_adapter(typename apply<F, TypeA>::type & value) : type(fmtA), valueA(&value) {}
    arg_adapter(typename apply<F, TypeB>::type & value) : type(fmtB), valueB(&value) {}
    // ...
};

typename arg_adapter< lambda< boost::add_const<_> >::type > const_adapter;
typename arg_adapter< lambda< boost::mpl::identity<_> >::type > nonconst_adapter;
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
9

I just stumbled about an even better way using the type selection ideom presentend by Alexandrescu in "Modern C++ Design":

This is the type selector:

template<bool flag, typename T, typename U>
struct Select { typedef T Result; }

template<typename T, typename U>
struct Select<false, T, U> { typedef U Result; }

Your class would then look like this:

template<bool isConst>
struct arg_adapter
{
  // define A and B as const or non-const
  typedef typename Select<isConst, const TypeA, TypeA>::Result A;
  typedef typename Select<isConst, const TypeB, TypeB>::Result B;

  EArgType type;  // fmtA, fmtB, ...

  union
  {
    A * valueA; // this is either const TypeA* oder TypeA* depending on
                // your choice of the isConst template parameter
    B * valueB;
    // ... more types
  }

  arg_adapter(A & value) : type(fmtA), valueA(&value) {} // same here with ref
  arg_adapter(B & value) : type(fmtB), valueB(&value) {}
  // ...
}

You can use typedefs for convenience:

struct nonconst_adapter : public arg_adapter<false> {};

struct const_adapter : public arg_adapter<true> {};

This was my old answer using simple type traits:

template<typename TypeTraits>
struct arg_adapter
{
  typedef typename TypeTraits::T T;
  void bar(T a) { ... } // by value/reference
  void bar(T* a) { ... } // by pointer
}

template<typename K>
struct NonConstTraits {
  typedef K T;
}

template<typename K>
struct ConstTraits {
  typedef const K T;
}

template<typename K>
struct nonconst_adapter : public arg_adapter<NonConstTraits<K> > {};

template<typename K>
struct const_adapter : public arg_adapter<ConstTraits<K> > {};
Danvil
  • 22,240
  • 19
  • 65
  • 88
  • thanks for the reply - it illustrates well what Johannes' solution using boost does. --- I didn't end up using it, as the amount of code with all the typedefs was abotu the same (and in a case of addition, some other places must be updated anyway). Still, I grew more confident of TMP. – peterchen Sep 13 '10 at 12:44
  • 1
    FYI -- As of the C++11 standard, you no longer need to define your own Select, since [std::conditional](https://en.cppreference.com/w/cpp/types/conditional) does exactly this. – joshtch Jul 29 '18 at 03:05
0

Maybe I didn't get it, but why can't you use

Convert(**const** arg_adapter from, arg_adapter to)

Declare a typedef to simplify the job

Gobra
  • 4,263
  • 2
  • 15
  • 20
  • "The constness of the object pointed to is a property of the template argument." This is not about const pointers here. – Wok Sep 10 '10 at 16:44
  • I need to call Convert with `(const TypeA &, TypeB &)`, and arg_adapter doesn't have a CTor for `const TypeA &`. – peterchen Sep 10 '10 at 21:44