1

consider the following mixings that provide additional functionality to the BaseSensor class.

class PeakSensor{ /*...*/ };
class TroughSensor{ /*...*/ };

template<typename EdgeType> //EdgeType can be PeakSensor or TroughtSensor
class EdgeSensor : public EdgeType
{
    public:
        void saveEdges(){}
}

class TrendSensor 
{   
    public:
        void saveTrends(){}
}

template<typename ... SensorType>
class BaseSensor : public SensorType ... //SensorType can be TrendSensor, EdgeSensor or others...
{
    public:
        void saveSensor();
}

where

template<typename ... SensorType>
void BaseSensor<SensorType...>::saveSensor()
{
    this->saveTrends();
    this->saveEdges();
}

and main.cpp

int main(int , const char **) 
{
    { //this works
        BaseSensor<EdgeSensor<TroughEdge> , TrendSensor> eps;
        eps.saveSensor();
        cout << endl;
    }

    { //this cannot not find "saveSensorEdges()", so it won't compile
        BaseSensor<TrendSensor> eps;
        eps.saveSensor();
        cout << endl;
    }
    return 0;
}

I have read that solutions involve following "SFINAE" rule however, the solutions in SO involve typing code specific to checking if a member function works (for example here). Is it possible to minimize coding by checking if the mixin class (ie TrendSensor or EdgeSensor) are included?

I am searching for a solution that minimizes additional coding (ie creating a multiple line struct just to check if a single method exists) in c++11 (boost may very well be used).

If this is not possible how could I check if the function exists for a specific instance and execute it (or not) accordingly.

Basically, can anything be placed in front of

    EXEC_ONLY_IF_EXISTS ( this->saveTrends(); )
    EXEC_ONLY_IF_EXISTS ( this->saveEdges(); )

in order to conditionally allow the code and execute it , or remove it altogether depending on whether the mixin is part of the instantiated object. thank you!

Community
  • 1
  • 1
nass
  • 1,453
  • 1
  • 23
  • 38

1 Answers1

2

You can call, in saveSensor(), a couple of new method: localTrends() and localEdges().

Then, you can develop two alternative implementations (SFINAE selected) of localTrends(); the first one, that call saveTrends(), enabled only when the TrendSensor is a base class of the actual class, and the second one, that doesn't call saveTrends(), otherwise (when TrendSensor isn't a base class).

Same strategy for localEdges(): two alternative implementations (SFINAE selected), the first one, that call saveEdges(), enabled only when the EdgeSensor<Something> is a base class of the actual class, and the second one, that doesn't call saveEdges(), otherwise (when EdgeSensor<Something> isn't a base class).

The SFINAE selection for localTrends() is easy, using std::is_base_of.

The SFINAE selection for localEdges() is a little more complicated because you can't (or at least: I don't know how to) check if EdgeSensor<Something> is a base class of the actual class using std::is_base_of because I don't know the Something class that is the template argument of EdgeSensor.

So I've developed a template struct, chkTplInL (for "checkTemplateInList") that receive a "template template" argument (that is EdgeSensor without its Something template argument) and a list of typenames. This struct set a constexpr static boolean value that is true if a class based on the "template template" argument (EdgeSensor, in our case) is in the list of typenames, (that, in our case, is: if a EdgeSensor class is base of the actual SensorType class), false otherwise.

The following is a working example

#include <type_traits>
#include <iostream>

class PeakSensor { };
class TroughSensor { };
class TroughEdge { };

template<typename EdgeType> 
class EdgeSensor : public EdgeType
{ public: void saveEdges(){} };

class TrendSensor 
{ public: void saveTrends(){} };

template <template <typename ...> class, typename ...>
struct chkTplInL;

template <template <typename ...> class C>
struct chkTplInL<C>
 { static constexpr bool value = false; };

template <template <typename ...> class C, typename T0, typename ... Ts>
struct chkTplInL<C, T0, Ts...>
 { static constexpr bool value = chkTplInL<C, Ts...>::value; };

template <template <typename ...> class C, typename ... Ts1, typename ... Ts2>
struct chkTplInL<C, C<Ts1...>, Ts2...>
 { static constexpr bool value = true; };

template<typename ... SensorType>
class BaseSensor : public SensorType ...
 {
   public:
      template <template <typename...> class C = EdgeSensor>
      typename std::enable_if<
         true == chkTplInL<C, SensorType...>::value>::type localEdges ()
       { this->saveEdges(); std::cout << "localEdges case A" << std::endl; }

      template <template <typename...> class C = EdgeSensor>
      typename std::enable_if<
         false == chkTplInL<C, SensorType...>::value>::type localEdges ()
       { std::cout << "localEdges case B" << std::endl; }

      template <typename B = TrendSensor>
      typename std::enable_if<
         true == std::is_base_of<B, BaseSensor>::value>::type localTrends ()
       { this->saveTrends(); std::cout << "localTrends case A" << std::endl; }

      template <typename B = TrendSensor>
      typename std::enable_if<
         false == std::is_base_of<B, BaseSensor>::value>::type localTrends ()
       { std::cout << "localTrends case B" << std::endl; }

      void saveSensor ()
       {
         this->localTrends();
         this->localEdges();
       }
 };


int main () 
 {
   BaseSensor<EdgeSensor<TroughEdge> , TrendSensor> eps1;
   eps1.saveSensor();  // print localTrends case A
                       // and   localEdges case A

   BaseSensor<TrendSensor> eps2;
   eps2.saveSensor(); // print localTrends case A
                      // and   localEdges case B

   BaseSensor<EdgeSensor<TroughSensor>> eps3;
   eps3.saveSensor(); // print localTrends case B
                      // and   localEdges case A

   BaseSensor<> eps4;
   eps4.saveSensor(); // print localTrends case B
                      // and   localEdges case B

   return 0;
 }

If you can use a C++14 compiler, you can use std::enable_if_t, so the SFINAE selection for localEdges() and localTrends() can be a little simpler

  template <template <typename...> class C = EdgeSensor>
  std::enable_if_t<
     true == chkTplInL<C, SensorType...>::value> localEdges ()
   { this->saveEdges(); std::cout << "localEdges case A" << std::endl; }

  template <template <typename...> class C = EdgeSensor>
  std::enable_if_t<
     false == chkTplInL<C, SensorType...>::value> localEdges ()
   { std::cout << "localEdges case B" << std::endl; }

  template <typename B = TrendSensor>
  std::enable_if_t<
     true == std::is_base_of<B, BaseSensor>::value> localTrends ()
   { this->saveTrends(); std::cout << "localTrends case A" << std::endl; }

  template <typename B = TrendSensor>
  std::enable_if_t<
     false == std::is_base_of<B, BaseSensor>::value> localTrends ()
   { std::cout << "localTrends case B" << std::endl; }
max66
  • 65,235
  • 10
  • 71
  • 111
  • hmm this cannot exactly work. I would like to run the mixin's save*() method, if the mixin has been built into the current object. If i am not mistaken, in this way I cannot create multiple instances of saveSensor and execute them all (one of each different mixin) – nass Sep 18 '16 at 21:45
  • also why do you introduce a new `PeakSensor()` ? That class should not contain anything related to saving. Such task is carried out my either EdgeSensor, or TrendSensor (or others) but not by Peak or Trough ... – nass Sep 18 '16 at 21:47
  • @nass - not sure to understand what do you mean with "I would like to run the mixin's save*() method, if the mixin has been built into the current object". Please, can you create a minimal but complete example? With all classes involved? I've introduced a new PeakSensor() just by example: I don't know were `saveSensorTrend()` and `saveSensorEdges()` are defined. – max66 Sep 18 '16 at 21:55
  • @nass - to respond to your question "can anything be placed in front of [...] in order to conditionally allow the code and execute it , or remove it altogether depending on whether the mixin is part of the instantiated object[?]", what about defining two local methods `localSaveSensorTrend()` and `localSaveSensorEdges()` with 2 alternative implemetation each (enabled/disabled via SFINAE) where `localSaveSensorTrend()` call `saveSensorTrend()` if available (version 1) or do nothing otherwise (version 2)? Same strategy with `localSaveSensorEdges()`. – max66 Sep 18 '16 at 22:02
  • oops serious error in question. Sorry about that. THere is no `saveSensorTrend()` instead there are only `saveTrend()`. similarly for the edges. I think this should fix it now. as for what meant, please see the edit at the end of the question: is it possible to do something like : `EXEC_ONLY_IF_EXISTS( this->saveTrends(); )` and similarly for edges ? – nass Sep 18 '16 at 22:02
  • Why would you write code that explicitly compares booleans "if true == true"? Where does it end? Why not "if (true == ( true == ( true == (true == true))))" – xaxxon Sep 18 '16 at 22:09
  • @nass - modified my anser to show my new ipothesys (two `localTrend()` member with SFINAE switch); if you consider it acceptable I can try to write `localEdges()` (more complicated (I think) because `EdgeSensor` is a template class) – max66 Sep 18 '16 at 22:17
  • @xaxxon - sorry: which `if ( true == true )` do you mean? – max66 Sep 18 '16 at 22:19
  • @max66 true == std::is_base_of>::value - I'm just giving you a hard time. It is easier, in an example, to make out the difference between the two versions this way. – xaxxon Sep 18 '16 at 22:23
  • @max66 These changes fail to compile. I get a `localTrend() cannot be overloaded`, on its "false" declaration in g++ with -std=c++11 . More errors follow afterwards. what could be wrong? – nass Sep 18 '16 at 22:26
  • @max66 scratch the above, I made it work, but simply adding similar calls for Edges , does not work. Why? – nass Sep 18 '16 at 22:35
  • @nass - you can try, but I don't know (I must think a little about it) how to manage the template part of `EdgeSensor`; In other words: I don't know the `EdgeType` – max66 Sep 18 '16 at 22:38
  • @xaxxon - not sure to undertand (sorry: English isn't my mother language). Are you saying that `enable_if< std::is_base_of [...] ::value, [...]` and `enable_if< ! std::is_base_of [...] ::value, [...]` are clearer than `enable_if< true == std::is_base_of [...] ::value, [...]` and `enable_if< false == std::is_base_of [...] ::value, [...]` ? – max66 Sep 18 '16 at 22:42
  • Yes. "is_base_of" is clearly a yes/no question, so asking if bool == bool is redundant. – xaxxon Sep 18 '16 at 22:43
  • the `EdgeType` mixins (ie `PeakSensor` and `TroughSensor`) know nothing about save() functionality. Is it somehow possible to fool a similar function say `localEdge()` with a dummy template arg for `EdgeSensor` ? – nass Sep 18 '16 at 22:43
  • @nass - modified my answer adding `localEdges()`; not so easy like `localTrend()` but, adding another class (`checkEdgeSensor`)... – max66 Sep 18 '16 at 23:02
  • @xaxxon - I see. Well, I suppose it's question of personal taste but I find `if ( true == boolVar )` as clear as `if ( boolVar )` but (limit of mine, I suppose) I find `if ( false == boolVar )` much more clear than `if ( ! boolVar )`; so when I have to check only against `true` value, I usually write `if ( boolVar )` but when I have to check both cases, I usually write `if ( true == boolVar )` and `if ( false == boolVar )` – max66 Sep 18 '16 at 23:08
  • this is it :) thank you. the code becomes HUGE , but it works :) – nass Sep 18 '16 at 23:13
  • @nass - With other template classes you can write other `checkSomething` structs or (better, I suppose) a generic `checkTemplateClass` that receive `EdgeSensor` (attention: `EdgeSensor`, not `EdgeSensor`) as a template template argument. Now I have to go but If you're interested, tomorrow I can try to modify my answer in a more general way. – max66 Sep 18 '16 at 23:14
  • @max66 I would be grateful - as I would like to know how to pass other classes in this checking system (And avoid lots of copy pastes). Thank you for tonight! – nass Sep 18 '16 at 23:19
  • @max66 also if possible please: how could I split the pure header stuff from the template's "pseudo" implementation stuff and move the latter into a file sensor.t.hpp ? thank you – nass Sep 18 '16 at 23:33
  • @nass - modified my anser; added `chkTplInL` struct, so you can use it with `EdgeSensor` and you can recycle it with other template class (with an undefined number of template arguments); semplified the code in some point; added a C++14 semplification – max66 Sep 19 '16 at 12:03
  • @nass - I don't know what do you mean exactly with "split the pure header stuff from the template's "pseudo" implementation stuff and move the latter into a file sensor.t.hpp" but I think that, with template class, it's a bad idea. Suggestion: ask another question showing a simplified, but working, example and saying what do you want to obtain. – max66 Sep 19 '16 at 12:06
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/123692/discussion-between-nass-and-max66). – nass Sep 19 '16 at 12:08