Reputation: 67
I have written a few template classes as part of a larger template class. One allows me to include or exclude member variables from a class. Another allows me to produce either the passed template class parameter or a placeholder that NOPs instead.
These allow classes to use their own template parameters to include or exclude data members through a single very generic class definition, and the operations provide an interface that prevents type mismatch issues.
This code sample shows my implementation in use as I assume it will be easier to recognise in action (the classes relating to the pattern have been called "ghost").
typedef int field1type, field2type, field3type;
template <bool field1exists, bool field2exists, bool field3exists>
struct GhostShowcase
{
typename Ghost<int, field1exists>::type intField;
typename Ghost<float, field2exists>::type floatField;
typename Ghost<char, field3exists>::type charField;
};
GhostShowcase<true,false,true> foo; // has int and char fields
// but an unusable float field
GhostShowcase<true,true,true> bar; // has all fields usable
The samples below show how to use operators with the types derived from ghost classes. The testing function printAll()
uses ghostStaticOperator()
to call static functions in a class derived from GhostStaticOperation
. This class is either the class that was passed as a template parameter to GhostStaticOperation
or an automatically generated class which has a static function with the same signature but does nothing. The class chosen is based on the boolean template parameter exists
. in main()
two calls are made; the first passes the template parameter true and the second passes false.
These samples show compilable source (in g++ 4.5.2, with -std=c++0x set). This file can be named anything.
#include "ghost.h"
#include <iostream>
#include <functional>
using namespace Aryana;
using namespace std;
struct PrintingOperator : public std::unary_function<int, void>
{
static inline void operation(int toPrint)
{cout << toPrint;}
};
struct IncrementOperator : public std::unary_function<int&, void>
{
static inline void operation(int& toIncrement)
{++toIncrement;}
};
template <bool exists>
void printAll()
{
typedef GhostStaticOperator<PrintingOperator, exists> printClass;
typedef GhostStaticOperator<IncrementOperator, exists> incrClass;
typename Ghost<int, exists>::type ghostObject;
cout << "using ghost printer: ";
ghostStaticOperation<printClass>(ghostObject);
cout << "\nusing ghost incrementor...\n";
ghostStaticOperation<incrClass>(ghostObject);
cout << "using ghost printer: ";
ghostStaticOperation<printClass>(ghostObject);
cout << "\nfinished\n";
}
int main(int, char**)
{
cout << "ghost operation with exists=true:\n";
printAll<true>();
cout << "ghost operation with exists=false:\n";
printAll<false>();
}
This sample should be named "ghost.h" and put in the same directory as the previous file.
typedef char ghost_null_argument[0];
typedef void ghost_null_return;
template <class S, bool exists>
class Ghost;
template <class S, bool exists>
class GhostOperator;
template <class S, bool exists>
class GhostStaticOperator;
template <class S>
class Ghost<S, false>
{
Ghost(); // private constructor to prevent instantiation
public:
typedef ghost_null_argument type;
typedef S original_type;
};
template <class S>
class Ghost<S, true>
{
Ghost(); // private constructor to prevent instantiation
public:
typedef S type;
typedef S original_type;
};
template <class S, bool exists>
class GhostOperator;
template <class S>
class GhostStaticOperator<S, false>
{
GhostStaticOperator(); // private constructor to prevent instantiation
public:
typedef ghost_null_return result_type;
typedef ghost_null_argument argument_type;
struct operator_type
{
inline static result_type operation(argument_type){};
};
};
template <class S>
class GhostStaticOperator<S, true>
{
GhostStaticOperator(); // private constructor to prevent instantiation
public:
typedef S operator_type;
typedef typename operator_type::result_type result_type;
typedef typename operator_type::argument_type argument_type;
};
// must define argument_type and result_type in the operator class
// result_type will be ghost_null_argument if the class is ghosting
template <class S>
inline
typename S::result_type ghostStaticOperation(typename S::argument_type argument)
{
return S::operator_type::operation(argument);
}
Upvotes: 0
Views: 293
Reputation: 179981
I'm fairly certain it's the "bug" pattern. char[0]
is not a valid member type.
More seriously, normally we use SFINAE to directly check for members that might or might not exist. That avoids the whole Ghost<T, bool>
template.
Upvotes: 1