tags:

views:

268

answers:

4

Is it possible to have a C++ template function which can access different fields in its input data depending on what type of input data was passed to it?

e.g. I have code of the form:

typedef struct
{
  int a;
  int b;
}s1;

typedef struct
{
  int a;
}s2;

template <class VTI_type> void myfunc(VTI_type VRI_data, bool contains_b)
{
  printf("%d", VRI_data.a);

  if(contains_b) // or suggest your own test here
    printf("%d", VRI_data.b); // this line won't compile if VTI_type is s2, even though s2.b is never accessed
}

void main()
{
  s1 data1;
  data1.a = 1;
  data1.b = 2;
  myfunc <s1> (data1, true);

  s2 data2;
  data2.a = 1;
  myfunc <s2> (data2, false);
}

So we want to use field A from many different data types, and that works fine.

However, some data also has a field B that needs to be used - but the code which accesses field B needs to be removed if the template knows it's looking at a data type that doesn't contain a field B.

(in my example, the structures are part of an external API, so can't change)

+3  A: 

Solution 1: You could use template specialization. The specialization might be performed on per-class basis, or on some more general trait.

EFraim
Providing the type is known at compile time.
xtofl
the type must be known at compile-time, since it's passed as a parameter to the template?
OJW
+5  A: 

To elaborate the suggested use of template specialization:

template <class T> void myfunc(T data)
{  
    printf("%d", VRI_data.a);  
}

// specialization for MyClassWithB:
template <>
void myfunc<MyClassWithB>(MyClassWithB data)
{
    printf("%d", data.a);  
    printf("%d", data.b); 
}

However, that requires a specialization per-class, there is no "auto-detection" of b. Also, you repeat a lot of code.

You could factor out that "having b" aspect into a helper template. A simple demonstration:

// helper template - "normal" classes don't have a b
template  <typename T>
int * GetB(T data) { return NULL; }  

// specialization - MyClassWithB does have a b:
template<>
int * GetB<MyClassWithB>(MyClassWithB data) { return &data.b; }

// generic print template
template <class T> void myfunc(T data)
{  
    printf("%d", VRI_data.a);  
    int * pb = GetB(data);
    if (pb)
      printf("%d", *pb); 
}
peterchen
A: 

If you don't want to hard code S1 & S2 specialization in the code then you can do:

typedef struct
{
  int a;
  int b;
}s1;

typedef struct
{
  int a;
}s2;

template <class T, bool contains_b> 
struct MyFunc
{

    void operator()(T data)
    {
     printf("%d", data.a);
    }

};


template <class T> 
struct MyFunc<T, true>
{
    void operator()(T data)
    {
     printf("%d", data.a);
     printf("%d", data.b); 

    }
};

template<class T, bool contains_b>
void myFunc(T t)
{
    MyFunc<T, contains_b> m;
    m(t);
}

int _tmain(int argc, _TCHAR* argv[])
{
 s1 data1;
  data1.a = 1;
  data1.b = 2;

  myFunc<s1,true>(data1) ;

  s2 data2;
  data2.a = 1;
  myFunc<s2,false>(data2);
  return 0;
}
Naveen
A: 

To elaborate on other peoples' answers, it seems the best way to do this without duplicating the same code across multiple templates is to just have an "extra stuff" function (empty for most data-types, but containing code for any objects which have additional data), where "template specialisation" chooses which "extra stuff" function to run for each data type.

Updated code:

typedef struct
{
  int a;
  int b;
}s1;

typedef struct
{
  int a;
}s2;

template <class VTI_type> void extraStuff(VTI_type VRI_data)
{
}

template <> void extraStuff<s1>(s1 VRI_data)
{
  printf(" and b = %d\n", VRI_data.b);
}

template <class VTI_type> void myfunc(VTI_type VRI_data)
{
  printf("a = %d\n", VRI_data.a);
  extraStuff(VRI_data);
}

void main()
{
  s1 data1;
  data1.a = 1;
  data1.b = 2;
  myfunc <s1> (data1);

  s2 data2;
  data2.a = 1;
  myfunc <s2> (data2);
}
OJW