4

Is there an equivalent of #ifdef to test if a member exists in a class so that processing can be done without causing the code to fail the compiler. I have tried template operations but the particular problem has not succeeded.

For example

#if member baseclass.memberA()
  baseclass.memberA().push_back(data);
#else
  doAlternate(data);
#endif

Obviously the above is not valid, but I am trying to discover if something like this has been added to C++11

Note that in the initial setup, there will exist memberA, memberB, memberC, ... each of which will require the push_back. Other members will be added to the baseclass in the future, which is why I want to create a template so that all the cases will compile and process properly even if the current baseclass does not have some of the members (such as memberX). Otherwise, I can just put in the push_back() line with a very simple template.

This is actually the simplest case. There is also the case in which I create an instantiation of the subclass and then push it back into the subclass member.

// Instantiate an element of the Maindata class
::basedata::Maindata maindata;
//Instantiate an element of the Subdata class
::basedata::Subdata subinfo("This goes into the subinfo vector");
// Process some data that is part of the Subdata class
subinfo.contexts(contextInfo);
// Push the instantiated Subdata into the Subdata member of Maindata
maindata.subdata().push_back(subinfo);

Note that both Subdata and subdata() need to be set up so that the appropriate code is implemented. However, if ::basedata::Subdata exists then so will maindata.subdata().

I have already tried various methods using templates and the particular problem has not been solvable with the various answers received. Examples are template instantiation check for member existing in class, C++ class member check if not a template, C++ template for variable type declaration

Community
  • 1
  • 1
sabbahillel
  • 4,357
  • 1
  • 19
  • 36
  • 3
    This can be done with templates, see http://stackoverflow.com/a/257382/1111028 – scaryrawr Feb 26 '15 at 17:31
  • @scaryrawr That answer does not solve the problem. I already tried it. I have been able to build a template solution **if** the member exists, but have not been able to get templates to work when the member does not exits. – sabbahillel Feb 26 '15 at 17:38

3 Answers3

5

This is just another case for void_t.

We need a little helper template Void and define a convenience template type alias void_t.

#include <type_traits>

template<typename...>
struct Void { using type = void; };

template<typename... T>
using void_t = typename Void<T...>::type;

We define the primary template that implements the fallback policy.

template<typename T, typename = void>
struct Helper
{
  static void
  function(T& t)
  {
    std::cout << "doing something else with " << &t << std::endl;
  }
};

And provide a partial specialization for types that support a specific operation, in this case, .data().push_back(int).

template<typename T>
struct Helper<T, void_t<decltype(std::declval<T>().data().push_back(0))>>
{
  static void
  function(T& t)
  {
    std::cout << "pushing back data to " << &t << std::endl;
    t.data().push_back(42);
  }
};

To hide the Helper implementation detail from our clients and to allow type deduction for the template parameters, we can nicely wrap it up.

template<typename T>
void
function(T& t)
{
  Helper<T>::function(t);
}

And this is how our clients use it.

#include <iostream>
#include <vector>

class Alpha
{
public:
  std::vector<int>& data() { return this->data_; }
private:
  std::vector<int> data_ {};
};

class Beta { /* has no data() */ };

int
main()
{
  Alpha alpha {};
  Beta beta {};
  std::cout << "&alpha = " << &alpha << std::endl;
  std::cout << "&beta  = " << &beta << std::endl;
  function(alpha);
  function(beta);
}

Possible output:

&alpha = 0x7ffffd2a3eb0
&beta  = 0x7ffffd2a3eaf
pushing back data to 0x7ffffd2a3eb0
doing something else with 0x7ffffd2a3eaf

Update: How to apply this technique to multiple members

The technique shown above can be applied to any number of members. Let's make up a little example. Say we want to write a template function frobnicate that takes an argument of generic type and if the object has…

  • …a member function incr that takes no arguments, call it,
  • …a data member name, append some text to it if possible and
  • …a data member numbers, push_back some numbers to it if possible.

I really recommend you solve this by implementing three helper structs as shown above. It is not that much redundant typing and makes for much cleaner code.

However, if you wish to ignore this advice, let's see how we can reduce the typing by using a macro. Assuming the same definition of void_t as shown above, we can define the following macro.

#define MAKE_SFINAE_HELPER(NAME, TYPE, OPERATION, ARGS, CODE)           \
  template<typename TYPE, typename = void>                              \
  struct NAME                                                           \
  {                                                                     \
    template<typename... AnyT>                                          \
    void                                                                \
    operator()(AnyT&&...) noexcept                                      \
    {                                                                   \
      /* do nothing */                                                  \
    }                                                                   \
  };                                                                    \
                                                                        \
  template<typename TYPE>                                               \
  struct NAME<TYPE, void_t<decltype(std::declval<TypeT>()OPERATION)>>   \
  {                                                                     \
    void operator()ARGS noexcept(noexcept(CODE))                        \
    {                                                                   \
      CODE;                                                             \
    }                                                                   \
  };

It will define a struct called NAME templated on a type parameter TYPE and define a primary template with an operator () that takes any number of arguments of any type and does absolutely nothing. This is used as the fallback if the desired operation is not supported.

However, if an object of type TYPE supports the operation OPERATION, then the partial specialization with an operator () that takes parameters ARGS and executes CODE will be used. The macro is defined such that ARGS can be a parenthesized argument list. Unfortunately, the preprocessor grammar only allows for a single expression to be passed as CODE. This is not a big problem as we can always write a single function call that delegates to another function. (Remember that any problem in computer science can be solved by adding an extra level of indirection – except, of course, for the problem of too many levels of indirection…) The operator () of the partial specialization will be declared noexcept if and only if CODE is. (This also only works because CODE is restricted to a single expression.)

The reason that the operator () for the primary template is a template is that otherwise the compiler might emit warnings about unused variables. Of course, you can alter the macro to accept an additional parameter FALLBACK_CODE that is placed in the body of the primary template's operator () that should use the same ARGS then.

In the most simple cases, it might be possible to combine the OPERATION and the CODE parameter into one but then CODE cannot refer to ARGS which effectively limits ARGS to a single parameter of type TYPE in which case you could get rid of that parameter as well, if you don't need the flexibility.

So, let's apply this to our problem. First, we need a helper function for pushing back the numbers because this cannot be written (at least, let's pretend this) as a single expression. I make this function as generic as possible, making only assumptions on the member name.

template<typename ObjT, typename NumT>
void
do_with_numbers(ObjT& obj, NumT num1, NumT num2, NumT num3)
{
  obj.numbers.push_back(num1);
  obj.numbers.push_back(num2);
  obj.numbers.push_back(num3);
}

Since the other two desired operations can easily be written as a single expression, we need no further indirection for them. So now, we can generate our SFINAE helpers.

MAKE_SFINAE_HELPER(HelperIncr,
                   TypeT,
                   .incr(),
                   (TypeT& obj),
                   obj.incr())

MAKE_SFINAE_HELPER(HelperName,
                   TypeT,
                   .name += "",
                   (TypeT& obj, const std::string& appendix),
                   obj.name += appendix)

MAKE_SFINAE_HELPER(HelperNumbers,
                   TypeT,
                   .numbers.push_back(0),
                   (TypeT& obj, int i1, int i2, int i3),
                   do_with_numbers(obj, i1, i2, i3))

Equipped with these, we can finally write our frobnicate function. It's really simple.

template<typename T>
void
frobnicate(T& object)
{
  HelperIncr<T>()(object);
  HelperName<T>()(object, "def");
  HelperNumbers<T>()(object, 4, 5, 6);
}

To see that everything works, let's make two structs that partially support the operations in question.

#include <string>
#include <vector>

struct Widget
{
  std::vector<int> numbers {1, 2, 3};
  int counter {};
  void incr() noexcept { this->counter += 1; }
};

struct Gadget
{
  std::string name {"abc"};
  int counter {};
  void incr() noexcept { this->counter += 1; }
};

Since I want to print them, let's also define operators <<.

#include <iostream>

std::ostream&
operator<<(std::ostream& os, const Widget& w)
{
  os << "Widget : { counter : " << w.counter << ", numbers : [";
  int i {};
  for (const auto& v : w.numbers)
    os << (i++ ? ", " : "") << v;
  os << "] }";
  return os;
}

std::ostream&
operator<<(std::ostream& os, const Gadget& g)
{
  os << "Gadget : { counter : " << g.counter << ", "
     << "name = \"" << g.name << "\" }";
  return os;
}

And there we go:

int
main()
{
  Widget widget {};
  Gadget gadget {};
  std::cout << widget << "\n" << gadget << "\n\n";
  frobnicate(widget);
  frobnicate(gadget);
  std::cout << widget << "\n" << gadget << "\n";
}

Output:

Widget : { counter : 0, numbers : [1, 2, 3] }
Gadget : { counter : 0, name = "abc" }

Widget : { counter : 1, numbers : [1, 2, 3, 4, 5, 6] }
Gadget : { counter : 1, name = "abcdef" }

I encourage you to carefully gauge the costs and benefits of this macro approach. In my opinion, the extra complexity is barely worth the small savings on the typing.

5gon12eder
  • 24,280
  • 5
  • 45
  • 92
  • In my question, the desired result is base.sub().push_back(data0 while you put data in the position of sub. base is always valid and I need to determine if sub is valid. The alternate needs to be outside the template in the calling routine. – sabbahillel Feb 26 '15 at 18:21
  • I don't see your point. You can replace the expression inside the `decltype` with whatever you want to test for whether it will compile. If you show me what your two alternatives are, I can update my answer accordingly but it should be a trivial transformation. – 5gon12eder Feb 26 '15 at 18:24
  • In this particular case, I would want to have template with T.U().push_back(data) If necessary I can initially keep T a constant value for testing. See also the more complicated case I added – sabbahillel Feb 26 '15 at 19:30
  • I neither understand your updated question nor your comment. What is `T.U().push_back()`? Do you mean that `U` is a nested type of `T` you want to create an instance of and then invoke `push_back` on it? It would be much easier if you provided an actual example for two types like my `Alpha` and `Beta` `structs`. – 5gon12eder Feb 26 '15 at 19:35
  • What I mean is that what you call Alpha always exists and has various members (A, B, C, ...) each of which has the same operation applied to it (push_back) within a switch and case. There will be additional members added in the future (X, Y, Z) but they are not yet in the library as members of Alpha. I can create a simple template if they do exist. However, I want to create a template to handle the members that are not yet part of Alpha. I do not want to have to create a test for each possible member. If you could make "memberA" the template variable, that is what I would want to do. – sabbahillel Feb 26 '15 at 20:05
  • What I meant was that the member is defined within the ::basedata include area. I create a local instantiation (subinfo) and then use the push_back oprater to include it within the proper vector of the main class. While I would like to do this within a single template, the initial need is for setting up the push_back() operation. – sabbahillel Feb 26 '15 at 20:09
  • Note that there will be future cases in which the Alpha will differ as well as the member name. However, that is not as critical. – sabbahillel Feb 26 '15 at 20:11
  • So you have a class (call it `Alpha`) that might or might not have members `A`, …, `Z` and for each of those individual members – if they are present – you want to invoke a function. If a member is not present, you want to do nothing to it but yet to the other members that are present? Basically, what you want is a `push_back_to_member_X_if_present()`? – 5gon12eder Feb 26 '15 at 20:24
  • yes that is correct. but as I said, without having to write multiple templates. I want a generic template. Also there may be multiple classes that I am doing this to (Alpha, Beta, etc) however, that is the next step. Also the names are not necessaril in any specific form. The use of memberA etc was only for an example. – sabbahillel Feb 27 '15 at 03:43
  • @sabbahillel Alright, I have updated the answer but I think you'll be better off writing a template for each member individually. It'll be so much cleaner. – 5gon12eder Feb 27 '15 at 23:27
  • You are correct. Having looked at the answers, it appears that he best way is to create a generic template that assumes that the member exists and then a template for each member that calls the generic template. The template for each member would then be only a thin wrapper. – sabbahillel Mar 01 '15 at 00:36
3

Not with preprocessor, but following may help:

#include <cstdint>

#define DEFINE_HAS_SIGNATURE(traitsName, funcName, signature)               \
    template <typename U>                                                   \
    class traitsName                                                        \
    {                                                                       \
    private:                                                                \
        template<typename T, T> struct helper;                              \
        template<typename T>                                                \
        static std::uint8_t check(helper<signature, &funcName>*);           \
        template<typename T> static std::uint16_t check(...);               \
    public:                                                                 \
        static                                                              \
        constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); \
    }

DEFINE_HAS_SIGNATURE(has_memberA, T::memberA, std::vector<int> (T::*)(void));

And then, using SFINAE:

#include <type_traits>

template <typename T>
std::enable_if<has_memberA<T>::value>::type
doBase(T& t, int data)
{
    t.memberA().push_back(data);
}

template <typename T>
std::enable_if<!has_memberA<T>::value>::type
doBase(T& , int data)
{
    doAlternate(data);
}
Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Thanks. I will try this and see what it does. Obviously I will not use the "do" keyword but will create a different name – sabbahillel Feb 26 '15 at 17:40
  • Do I have to write a separate template in the enable_if for each member or do I need to create another macro. The main point is that I want to set up a generic situation since I have a number of cases each with its own member. – sabbahillel Feb 26 '15 at 19:27
  • @sabbahillel: the macro is here to allow the creation of the traits in one line. then you have to use `enable_if` for each situation according to your traits. – Jarod42 Feb 26 '15 at 19:49
  • Is there some whay in which I can set up for memberA, memberB, ... since I would not want to have to create a new one as new members are added to the base class. That is the main point of the question. – sabbahillel Feb 26 '15 at 20:18
0

I finally figured out based on the answers to this and other questions, that I really have two questions that require two different answers.

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

::basedata::Maindata maindata;
maindata.subdata().push_back(subinfo);
auto inData maindata.subdata().back();
inData.contexts(contextInfo);

The template is of the form (which assumes that the class subdata() does exist (such as memberA in the question).

template<typename T, typename D>
auto doPush(D myData, T & myFormat, contextType contextInfo)
  -> decltype(myFormat.push_back(myData), bool())
{
  myFormat.push_back(myData);
  setcontexts(myFormat.back(), contextInfo)
  // Since the push-back() was already done, the new data gets entered
  return true;
}

This makes the call to the template

doPush(dataset, maindata.subdata(), contextInfo);

Since this assumes that subdata() exists, we now need to set up an explicit test for the member represented by subdata and make it a thin wrapper around the call to the generic template.

template<typename T, typename D>
auto createMember(D myData, T & myFormat, contextType contextInfo)
  -> decltype(myFormat.Member(), bool())
{
  dopush(myData, myFormat.Member(), myData);
  return true;
}

Note that this requires only three places for the member name to be input via a macro if there are enough locations to require one.

The actual code would then call the createMember template.

It appears that this would be the simplest solution.

I did not show the false case templates as those are obvious.

sabbahillel
  • 4,357
  • 1
  • 19
  • 36