0

I have a number of classes with differing members, all of which have operations of the following type

::basedata::Maindata maindata;
::basedata::Subdata subinfo("This goes into the subinfo vector");
subinfo.contexts(contextInfo);
maindata.subdata().push_back(subinfo);

Note that I am asking how to set up generalized templates to perform these actions. I cannot set up a special case for each type of maindata and subinfo. I also need to be able to see how to call the template from my main code. I have been able to set up a template if maindata.subdata() exists, but keep getting a compilation failure on a call to the template if it does not exist. That is create the template of the form

perform_Push(maindata.subdata(), subinfo);

so that it can be compiled whether or not maindata.subdata() exists or not.

I could accept templates that build so that the main code can show

bool retval
retval = hasmember(maindata, subdata);
if (retval)
  {
    buildmember(maindata.subdata, subinfo);
    setAttributes(subinfo, data);
    perform_Push(maindata.subdata(), subinfo)
  }
else
  {
    // Perform alternate processing
   }

As of now, the code inside the if would not compile when the templates being called should just be void.

While ::basedata::Maindata is always defined, ::basedata::Subdata may or may not be defined depending on the release of libraries that my code is being build with. subdata is defined as a vector belonging to maindata which therefore has the push_back() operation defined. There are too many types of subData to create a separate template for each type as T::Subdata within a template in any case.

That is, if subdata was the only case, I could create a specialization of the template T as ::maindata::subdata and a generic Template T.

I do not have any control of the include files or library that for this so that I cannot create a #define of a variable to test with the pre-compiler. Is there a good way of setting up a template that would allow this to work? I could use a template that returns a boolean true (success) or false (no such definition) and call the alternate processing at run time. I would not need to have an alternate template.

Basically, I am asking how to apply SFINAE to this particular situation.

I have managed to figure out what I need to do to set up the basic template

If I have the most basic operation

maindata.subdata().push_back(data)

I can define a template of the form,

<template class T, typename D>
auto doPush(D data) -> decltype(T.pushback(data), void())
{
  T.push_back(data);
}

and the call would be

doPush<maindata.subdata()>(data);

However, the problem would be how to set it up when maindata does not yet have a member subdata.

sabbahillel
  • 4,357
  • 1
  • 19
  • 36

2 Answers2

1

You can use this templates to obtain a boolean value that tell you if exist member type Subdata in a generic type T. This works only if T is a struct/class not a namespace.

#include <type_traits>

template <class T, class V = void>
  struct hasSubdata
  {
    enum { value = false }; 
  };

template <class T>
  struct hasSubdata<T, typename std::enable_if< std::is_same<typename T::Subdata, typename T::Subdata>::value >::type>
  {
    enum { value = true }; 
  };


struct basedata1
{
  struct Subdata {};
};

struct basedata2
{

};

#include <iostream>

int main ()
{
  std::cout << "basedata1: " << hasSubdata<basedata1>::value << std::endl;
  std::cout << "basedata2: " << hasSubdata<basedata2>::value << std::endl;
}

But you can't use a normal if because the compiler checks the correctness of all the possibilities. You have to act in a similar way (pretty ugly):

template <class T, bool = hasSubdata<T>::value>
  struct SubdataUser
  {
    static void foo ()
    {
      std::cout << "I can use SubData member :)" << std::endl;

      typename T::Subdata subinfo ();
    }        
  };

template <class T>
  struct SubdataUser<T, false>
  {
    static void foo ()
    {
      std::cout << "I can not :(" << std::endl;
    }        
  };

int main ()
{
  SubdataUser<basedata1>::foo ();
  return 0;
}

Unfortunately to my knowledge, you can not have a template hasMember<Type,Member>::value because if Member does not exist, compilation fails.

But you might like a solution of this type

#include <type_traits>
#include <iostream>

struct basedata1
{
  struct Subdata1 {};
  struct Subdata2 {};
  struct Subdata3 {};
};

struct basedata2
{
  struct Subdata1 {};
  //struct Subdata2 {};
  struct Subdata3 {};
};

template <class...>
  struct Require
  {
    enum { value = true };
  };

template <class T, bool = true>
  struct Impl
  {
    static void foo ()
    {
      std::cout << "At least one of the members required is not available :(" << std::endl;
    }        
  };

template <class T>
  struct Impl<T, Require< typename T::Subdata1,
                          typename T::Subdata2,
                          typename T::Subdata3 >::value >
  {
    static void foo ()
    {
      std::cout << "All members are available :)" << std::endl;

      typename T::Subdata2 my_var;
    }        
  };


int main( int argc, char* argv[] )
{
  Impl<basedata1>::foo ();
  Impl<basedata2>::foo ();
  return 0;
}

I hope this helps

  • Thank you. This looks as if it would work. Do you have a way of setting up given that baseData and subData are both different settings so that baseA::subBaseA and baseDataB::subBaseB can be set up or must I check each one separately and explicitly? – sabbahillel Feb 23 '15 at 22:54
  • I see that this gives a result for explicitly Subdata as a member of the generic class T. However, how do I set it up to get Subdata as a Template member. That is, membeA, memberB, memberC need to be checked and processing done. If there is a result of false, how do I set up the call in my main code to actually do the check? – sabbahillel Feb 24 '15 at 22:30
  • I have added another solution. You can not write a template `hasMember::value` because only a template parameter substitution can fail without an error. If the type is explicitly written and does not exist, compilation fails. – Alberto Boldrini Feb 25 '15 at 17:28
  • Is there a way to set up a template which uses template and then sets up the call to refer to baseClass::T instead of T::subClass? – sabbahillel Feb 25 '15 at 21:17
  • I added an addendum to the question that seems to point to an answer. – sabbahillel Feb 26 '15 at 14:22
0

I have managed to figure out what I need to do to set up the basic template as well as the member template. It is actually two different questions and two different answer templates. It requires a basic generic template called by a specific member template.

C++ preprocessor test if class member exists

Community
  • 1
  • 1
sabbahillel
  • 4,357
  • 1
  • 19
  • 36