Ken
Ken

Reputation: 719

Execute or skip code based on template parameter contents

I want to create a container for an arbitrary type T. However, I want to add some functionality if T has a member that is header type (which I also define). If T does not have that header member, then the added functionality can just be skipped.

The added functionality might, for example, be to add a timestamp based on when an operation is performed. Here's pseudo code for what I want:

struct my_header {
  timestamp_t time;
  // ... etc ...
}

template <class T>
class my_container {
  public:
    void some_operation(T val) {
        // condition evaluated at compile time
        if T has a member of type my_header {
            val.header.time = get_current_time();
            // ... etc ...
        }

        // other operations, agnostic to T
    }
};

of course as I have it, some_operation also has to figure out the name of the instance of my_header in class T. This requirement could be eliminated by imposing one of the following requirements for the added functionality to be used (in order from most to least preferable):

Using C++11 is fine (expected in fact).

Upvotes: 4

Views: 774

Answers (2)

Daniel Frey
Daniel Frey

Reputation: 56863

remyable's solution is not bad (+1), but it can be simplified:

#include <iostream>
#include <type_traits>

struct my_header {
  int time = 0;
};

template<typename T>
using has_header = std::is_same< decltype( T::header ), my_header >;

struct foo
{
    my_header header;
};

template<typename T, typename = void>
class my_container
{
public:
    T val;
    void foo()
    {
        std::cout << "other.\n";
    }
};

template<typename T>
class my_container<T, typename std::enable_if<has_header<T>::value>::type>
{
public:
    T val;
    void foo()
    {
        std::cout << "time: " << val.header.time << "\n";
    }
};

int main()
{
    my_container<foo> c;
    my_container<int> c2;
    c.foo(); // time: 0
    c2.foo(); // other.
}

Live example

Of course this solution still uses the requirement that the member variable is called header is C++ does not have introspection which can iterate a type's members.

Upvotes: 1

user1508519
user1508519

Reputation:

Not the best solution, but I think it can work. Stolen code from How to detect whether there is a specific member variable in class?

#include <iostream>
#include <type_traits>

struct my_header {
  int time;
};

// begin stolen code
template<typename T, typename V = bool>
struct has_header : std::false_type { };

template<typename T>
struct has_header<T, 
    typename std::enable_if<
        !std::is_same<decltype(std::declval<T>().header), void>::value, 
        bool
        >::type
    > : std::true_type { };
// end stolen code

struct foo
{
    my_header header;
};

template<typename, typename = void>
class my_container;

template<typename T>
class my_container<T, typename std::enable_if<has_header<T>::value>::type>
{
public:
    T val;
    void foo()
    {
        std::cout << val.header.time << "\n";
    }
};

template <typename T>
class my_container<T, typename std::enable_if<!has_header<T>::value>::type>
{
public:
    T val;
    void foo()
    {
        std::cout << "other.\n";
    }
};

int main()
{
    my_container<foo> c;
    my_container<int> c2;
    c.foo(); // garbage
    c2.foo(); // other.
}

Upvotes: 1

Related Questions