
#include <iostream>
#include <list>

template <typename argT>
struct arg {
private:
  template <typename T, bool big=false> 
  struct choice {
    typedef T pass;        // small object by value
  };
  template <typename T>
  struct choice<T, true> {
    typedef T const &pass; // large object by reference
  };
public:
  static unsigned int const cutoff_size=4*sizeof(void *);
  typedef typename
    choice<argT, (sizeof(argT)>cutoff_size)>::pass
    pass;
};


#define arg_pass(type) typename arg<type>::pass

///////////////////////
// testing apparatus //
///////////////////////

template <typename T>
struct ArgAddress {
  static void const *get(arg_pass(T) t) {
    void const *address=static_cast<void const *>(&t);
    return address;
  }
};

#define var_address(var) static_cast<void const *>(&var)

#define check_type(type)                                  \
do {                                                      \
  std::cout << "type \"" << #type << "\" ";               \
  type object;                                            \
  if (var_address(object)==ArgAddress<type>::get(object)) \
    std::cout << "by reference";                          \
  else                                                    \
    std::cout << "by value";                              \
  std::cout << std::endl;                                 \
} while (false)

struct Small {
  char c;
};

struct Big {
  char array[1000];
};

struct FourPointers {
  typedef void *Pointer;
  Pointer array[4];
};

struct FivePointers {
  typedef void *Pointer;
  Pointer array[5];
};

struct SmallButExpensive {
  SmallButExpensive() { }
  SmallButExpensive(SmallButExpensive const &) {
    // expensive copy-construction
  }
  char c;
};

template <>
struct arg<SmallButExpensive> {
  typedef SmallButExpensive const &pass;
};

struct BigButCopy {
  char array[1000];
};

template <>
struct arg<BigButCopy> {
  typedef BigButCopy pass;
};


template <typename T>
struct arg<std::list<T> > {
  typedef std::list<T> const &pass;
};


int main() {
  check_type(char);
  check_type(double);
  check_type(Small);
  check_type(Big);
  check_type(FourPointers);
  check_type(FivePointers);
  check_type(SmallButExpensive);
  check_type(BigButCopy);
  check_type(std::list<char>);
}



struct ChessBoard {
  char square[8][8];
};
int count_pawns_value(ChessBoard board);
int count_pawns_reference(ChessBoard const &board);



template <typename T>
class Predicate {
public:
  ~Predicate() { }
  virtual bool operator()(T)=0;
};


/* not expected to compile

void function(T x);


void function(T const &x);


void function(arg<T> x);

*/

template <typename T>

void function(typename arg<T>::pass x);

template <typename T>

void function(arg_pass(T) x);


