11

In my project, I want to implement a template proxy class of some existing bigger classes. The existing classes are library classes, so they can not be modified. In most cases, the clients do not know the objects are instances of proxy class or bigger class. In some cases, however, the clients MUST know the detailed class information. Since the proxy class is itself a template class, I do not think simple function overloading by class name could solve this problem. The possible solution I thought is to add an internal nested class or typedef inside the proxy class, and the client check whether this class/typedef exists to get the class information. My question is: how to check whether a class has specified nested class definition or typedef in C++ 11 ?

The following codes show an example:

#include <iostream>
#include <functional>
#include <string>
#include <vector>
#include <type_traits>

typedef std::string CBig1;  //  use string for demonstration
typedef std::string CBig2;  //  use string for demonstration

//class CBig1;   // the bigger class 1, codes of which can not be changed
//class CBig2;   // the bigger class 2, codes of which can not be changed

template <typename _Big, typename _Other>
class CProxy
{
public:
    struct proxy_tag { };
};

//  how to implement this ?
//  the proxy traits class, if defined _T::proxy_tag, the ``type'' will be std::true_type, otherwise the ``type'' will be std::false_type
template <typename _T>
struct is_proxy
{
    //typedef std::true_type type;
    //typedef std::false_type type;
};

template <typename _T>
void ClientHelp(const _T& t, std::false_type)
{
    //  process real class
    std::cerr << "real class" << std::endl;
}

template <typename _T>
void ClientHelp(const _T& t, std::true_type)
{
    //  process proxy class
    std::cerr << "proxy class" << std::endl;
}

template <typename _T>
void Client(const _T& t)
{
    ClientHelp(t, typename is_proxy<_T>::type());
}

int main(int argc, char* argv[])
{
    CBig1 b;
    CProxy<CBig1, int> p;
    Client(b);
    Client(p);
    return 0;
}

How to implement the traits class is_proxy?

Cœur
  • 37,241
  • 25
  • 195
  • 267
Yun Huang
  • 4,256
  • 7
  • 27
  • 36

2 Answers2

23

You can use the lightweight type categorization idiom

template<class T, class R = void>  
struct enable_if_type { typedef R type; };

template<class T, class Enable = void>
struct test : std::false_type {};

template<class T>
struct test<T, typename enable_if_type<typename T::is_proxy_tag>::type> : std::true_type
{};

template <typename _Big, typename _Other>
class CProxy
{
  public:
  typedef void is_proxy_tag;
};

So to make a class a Proxy, just add this

typedef void is_proxy_tag;

and the SFINAE in enable_if_type will select the proper true_type/false_type specialisation

Note that using boost::mpl::true_ instead of std::true_type etc makes this solution work for C++03.

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
Joel Falcou
  • 6,247
  • 1
  • 17
  • 34
  • Of course, nowadays, there is std::enable_if_t that one can use instead of enable_if_type above. – Hossein Jun 20 '19 at 14:38
  • nope. Look at what the enable_if_type does. It takes a type instead of a bool. If you want to replace this with modern C++, use std::void_t – Joel Falcou Jul 15 '19 at 13:50
13

As a complement to the C++03 version, in C++11 you get decltype:

template <typename T>
auto is_proxy(T const&) -> decltype(T::proxy_tag{}, std::true_type{}) {
  return std::true_type{};
}

std::false_type is_proxy(...) { return std::false_type{}; }

And your implementation of Client becomes:

template <typename T>
void Client(T const& t) {
  ClientHelp(t, is_proxy(t));
}

Sweet, isn't it ?

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
  • 1
    This doesn't compile for me (fairly recent clang++ from SVN). The problem seems to be the **two** arguments to `decltype()`. How's that supposed to work? – Michael Wild Apr 16 '12 at 07:49
  • 4
    @MichaelWild: it uses the sequence operator `,`. You generally find it in loop conditions, such as `for (;; ++i, ++e)`, the sequence operator evaluates both operands from left to right and returns the result of the right operand. It can be chained too: `++a, ++b, ++c` will return the result of `++c`, and you are guaranteed that the order of evaluation is `++a` *then* `++b` *then* `++c`. Here the only use is to have on the left part the SFINAE check and on the right the expression yielding the desired return type. You can try wrapping the whole thing in another layer of parentheses. – Matthieu M. Apr 16 '12 at 08:01
  • I suspected as much (I wasn't sure how the compiler was doing the parse here). Perhaps I need to add extra parenthesis? But then AFAIK, `decltype` is pretty peculiar when it comes to parenthesis... – Michael Wild Apr 16 '12 at 08:08
  • Ah. clang++ and g++ don't like the initialiser list. Using normal parenthesis works like a charm. – Michael Wild Apr 16 '12 at 09:15