3

following this question , I am trying to avoid copy-pasting some code related to calling all of the same-named methods of the mixins of the class BaseSensor.

in sensor.hpp

struct EdgeSensor //a mixin
{
    void update(){}
    void printStats() {}
};

struct TrendSensor //another mixin
{
    void update(){}
    void printStats() {}
};

template<typename ... SensorType>
class BaseSensor : public SensorType ... //to my BaseSensor class
{
    void update() /*{ what goes in here??? }*/
    void printStats() /*{ what goes in here??? }*/
};

in sensor.t.hpp

template<typename ... SensorType>
void BaseSensor<SensorType...>::update()
{
    int arr[] = { (SensorType::update(), 0)..., 0 };
    (void)arr;
}

template<typename ... SensorType>
void BaseSensor<SensorType...>::printStats()
{
    int arr[] = { (SensorType::printStats(), 0)..., 0 };
    (void)arr;
}

in main.cpp

int main(int , const char **) 
{
    {
        BaseSensor<EdgeSensor,TrendSensor> ets;
        ets.update();
        ets.printStats();
    }
    {
        BaseSensor<EdgeSensor> ets;
        ets.update();
        ets.printStats();
    }
}

The above code executes the update() of all the mixins in turn, before going on to execute all the printStats() from all the mixins as well.

I wonder if it is somehow possible to avoid duplicating the implementation of BaseSensor::update() and BaseSensor::printStats() and create a generic (template) function that accepts the name of the target function to execute across all the mixins:

For example, I could create a method runAll()

template<typename ... SensorType>
class BaseSensor : public SensorType ... //to my BaseSensor class
{
    void update() /*{ what goes in here??? }*/
    void printStats() /*{ what goes in here??? }*/

    template<typename FnName>
    void runAll(FnName f)
    {
        int arr[] = { (SensorType::f(), 0)..., 0 };
        (void)arr;
    }
};

How would I call it then from BaseSensor::update() and BaseSensor::printStats(). I have attempted to use

void update() { runAll<update>(); }
void printStats() { runAll<printStats>(); }

but this does not work (did not expect it to). The problem with passing function name as a function argument (which I see is many other questions such as here is that I do not know how to point to various ::update() functions from BaseSensor::update(). for example

void update() { runAll<update>( update() ); }

is also not correct.

Is it possible to avoid copying in this case? How would the template parameters look like if I where to move a working runAll() into file "sensor.t.hpp" ?

Thank you

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

5 Answers5

4

You can use a generic lambda and a kind of inversion of control.
It follows a minimal, working example:

#include<iostream>

struct EdgeSensor
{
    void update() { std::cout << "EdgeSensor::update" << std::endl; }
    void printStats() { std::cout << "EdgeSensor::printStats" << std::endl; }
};

struct TrendSensor
{
    void update() { std::cout << "TrendSensor::update" << std::endl; }
    void printStats() { std::cout << "TrendSensor::printStats" << std::endl; }
};

template<typename ... SensorType>
class BaseSensor : public SensorType ...
{
    template<typename F>
    void execute(F &&f) {
        int arr[] = { (f(static_cast<SensorType&>(*this)), 0)..., 0 };
        (void)arr;
    }

public:
    void update() {
        execute([](auto &t) { t.update(); });
    }

    void printStats() {
        execute([](auto &t) { t.printStats(); });
    }
};

int main() {
    BaseSensor<EdgeSensor,TrendSensor> ets;
    ets.update();
    ets.printStats();
}
skypjack
  • 49,335
  • 19
  • 95
  • 187
  • The lambda parameter should probably be a reference. – Sam Varshavchik Sep 18 '16 at 15:12
  • I see no reason why an ordinary reference will not work. The `static_cast` gives you that reference. – Sam Varshavchik Sep 18 '16 at 15:16
  • @SamVarshavchik Oh, sorry, I just re-read the comment. You are right. You said _the lambda parameter_. I misread the comment. Thank you for having pointed it out. – skypjack Sep 18 '16 at 15:20
  • Hi there, could you correct me in elaborating the above abit? You basically run a lambda fn which runs (or prepares to run?) `t.update()` . Then the "this" points to the lambda function ? and the actual execution of the lambda takes place during initiation of the array? – nass Sep 18 '16 at 15:22
  • @nass I guess I didn't understand the question. Anyway, the idea is to pass a lambda as a parameter and let the _executor_ invokes that function with the right types, so that you don't have to care about how many and what are those types. – skypjack Sep 18 '16 at 15:30
  • @skypjack the executor (as in the actual execution) takes place during the initialization of the array? – nass Sep 18 '16 at 15:32
  • @nass The _executor_ being the function `execute`. Ignore the array, it is discarded and it should not bias your attention. – skypjack Sep 18 '16 at 15:34
  • hm I cannot use auto. see this question : http://stackoverflow.com/questions/7709894/using-auto-in-a-lambda-function Switching to "SensorType" also has problems with unpacking – nass Sep 18 '16 at 15:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/123638/discussion-between-nass-and-skypjack). – nass Sep 18 '16 at 15:41
  • @nass That question is outdated. You can use `auto` as a type of a parameter of a lambda since C++14. See [generic lambda](http://en.cppreference.com/w/cpp/language/lambda) for further details. – skypjack Sep 18 '16 at 15:42
  • Hi may I point you to this question in case you have some idea? http://stackoverflow.com/questions/39650751/variadic-templates-with-template-function-names-and-passing-arguments-and-return/39651347#39651347 – nass Sep 23 '16 at 14:31
  • @nass Added an answer, hoping it can help you, I cannot spend more time on that. – skypjack Sep 23 '16 at 15:13
3

If you can use c++1z fold expressions would probably be the shortest way:

template<typename ... SensorType>
class BaseSensor : public SensorType ... //to my BaseSensor class
{
public:
    void update() { (SensorType::update(),...); }
    void printStats() { (SensorType::printStats(),...); }
};
W.F.
  • 13,888
  • 2
  • 34
  • 81
2

Another c++11 way could be to use std::array of pointer to method e.g.:

template<typename ... SensorType>
class BaseSensor : public SensorType ... //to my BaseSensor class
{
   void runAll(std::array<void (BaseSensor::*)(), sizeof...(SensorType)>&& vs) {
      for (auto v: vs) {
         (this->*v)();
      }
   }

public:
    void update() {
       runAll({&SensorType::update...});
    }
    void printStats() {
       runAll({&SensorType::printStats...});
    }
};
W.F.
  • 13,888
  • 2
  • 34
  • 81
  • actually this is beautiful :) – nass Sep 18 '16 at 19:12
  • 1
    Good one, but I'd use a `std::array` for the size is known and it doesn't require to allocate anything: `std::array&&` – skypjack Sep 18 '16 at 19:36
  • Also, `for (auto &&v: vs) {` instead of `for (auto v: vs) {` to avoid copies at all. – skypjack Sep 18 '16 at 19:37
  • Even better, no vectors and no arrays at all: `using Ptr = void (BaseSensor::*)();` and `void runAll(Ptr(&&vs)[sizeof...(SensorType)]) {`. – skypjack Sep 18 '16 at 19:50
  • @skypjack Yep `std::array` is a good point... but does it really make any difference if we avoid to copy the pointer by using reference... wouldn't it actually work exactly the same? – W.F. Sep 18 '16 at 19:50
  • 1
    Anyway, note what follows. [Here](https://godbolt.org/g/vPT3Cr) is the version using lambda, [here](https://godbolt.org/g/WOZazj) is the C++11 that uses a functor and [here](https://godbolt.org/g/cfmTWs) is the version in this answer. Look at the generated code. What do you notice? This version, even if optimized, is far more expensive. Quite curious indeed, but it's worth to be mentioned. ;-) – skypjack Sep 18 '16 at 19:55
  • @skypjack yes indeed... It could be because of the inlining lambda braces operator... nice catch :) – W.F. Sep 18 '16 at 20:09
  • @skypjack still fold expressions beat every implementation to a pulp :) – W.F. Sep 18 '16 at 20:23
  • 1
    @W.F. If you try them on godbolt, you'll see that the generated code is almost the same as using lambdas or a functor (once optimized, of course). Quite incredibly actually. [Here](https://godbolt.org/g/8FtqkP) it is. – skypjack Sep 18 '16 at 20:43
  • 1
    I missed the optimisation :) Wonder why array version is so huge... Another pro for lambdas :) – W.F. Sep 18 '16 at 20:48
2

Here is another, more compact solution that compiles in C++11:

#include <type_traits>
#include <iostream>

struct EdgeSensor {
    void update() { std::cout << "EdgeSensor::update" << std::endl; }
    void printStats() { std::cout << "EdgeSensor::printStats" << std::endl; }
};

struct TrendSensor {
    void update() { std::cout << "TrendSensor::update" << std::endl; }
    void printStats() { std::cout << "TrendSensor::printStats" << std::endl; }
};

template<typename ... SensorType>
class BaseSensor : public SensorType ... { 
    template <void(SensorType::* ...M)()>
    void run() {
        int arr[] = { 0, ((this->*M)(), 0)... };
        (void)arr;
    }

public:   
    void update() {   
        run<&SensorType::update...>();
    }

    void printStats() {
        run<&SensorType::printStats...>();
    }
};

int main() {
    BaseSensor<EdgeSensor, TrendSensor> bs;
    bs.update();
    bs.printStats();
}

I would be tempted to say that you don't need any structure to support the pack of pointers to member methods.
Anyway, I found that this compiles with clang and it doesn't work with GCC. I'm still trying to figure out if the code is ill-formed or if the problem is in the compiler.

I'd suggest to follow the other question to know if you can use or not this code.

Community
  • 1
  • 1
skypjack
  • 49,335
  • 19
  • 95
  • 187
1

Yet another pure c++11 answer which came to my mind. This one uses tag dispatching and non-type template parameters:

template <class T, void (T::*)()>
struct Method { };

template<typename ... SensorType>
class BaseSensor : public SensorType ... //to my BaseSensor class
{
   template <class T, void(T::*M)()>
   int runSingle(Method<T, M>) {
      (this->*M)();
      return 0;
   }

   template <class... Ts>
   void runAll() {
      int run[sizeof...(Ts)] = { runSingle(Ts{})... };
      (void)run;
   }

public:
    void update() {
       runAll<Method<SensorType, &SensorType::update>...>();
    }
    void printStats() {
       runAll<Method<SensorType, &SensorType::printStats>...>();
    }
};

It has to be stated though that non of these answers apart from the fold expressions (including skypjack's one) would deal with the virtual callee method of a mixin class... However I think skypjack answer could be easily modified to achieve such an effect:

#include<type_traits>

// (...)

template<typename ... SensorType>
class BaseSensor : public SensorType ...
{
    template<typename F>
    void execute(F &&f) {
        int arr[] = { (f(static_cast<SensorType&>(*this)), 0)..., 0 };
        (void)arr;
    }

public:
    void update() {
        execute([](auto &t) { t.std::remove_reference<decltype(t)>::type::update(); });
    }

    void printStats() {
        execute([](auto &t) { t.std::remove_reference<decltype(t)>::type::printStats(); });
    }
};
W.F.
  • 13,888
  • 2
  • 34
  • 81
  • hi there, could we go through your solution abit? What exactly is `Method` doing there? How can I interpret it? I am somewhat confused :) – nass Sep 22 '16 at 22:06
  • Of course `Method` is a kind of container type to carry the information about a single member function... It does not store any information is something rather similar to a tag in [tag dispatching](http://stackoverflow.com/documentation/c%2b%2b/462/metaprogramming/4332/tag-dispatching#t=201609222209598035076) – W.F. Sep 22 '16 at 22:10
  • However the solution isn't tag dispatching as it does not pass the compile-time information through the parameters to a function (`runAll`), but through a template parameters... – W.F. Sep 22 '16 at 22:13
  • `Method` carries out both information about `SensorType` as well as the pointer to the member function. This can be done as templates apart from plain old types can hold non-type parameters that meet [conditions](http://stackoverflow.com/documentation/c%2b%2b/460/templates/16713/non-type-template-parameter#t=201609222216050283468) (they generally need to be statically accessible) – W.F. Sep 22 '16 at 22:16
  • by `Method...` we unpack the `SensorType` pack in a dual way - we pick the `SensorType` itself to let `Method` know to who member function belongs and then we pass the pointer to the method... If we could use c++17 we wouldn't have to pass the sensor type to the `Method` as we could use [auto template parameters](http://stackoverflow.com/documentation/c%2b%2b/460/templates/16205/declaring-non-type-template-arguments-with-auto#t=201609222216050283468) there... – W.F. Sep 22 '16 at 22:25
  • When we unpack all the pointers we are ready to run each single method... and finally we are using tag dispatching by calling `runSingle` with given `Method` :) to run a method `(this->*M)()` on the object `this` Hope that explanation helps... – W.F. Sep 22 '16 at 22:28
  • ok coorrect me if I am wrong. The main idea is to create a pair of mixin and its function that we want to run. So we adapt the variadic template arguments into pairs of each template argument along with its method that we want to run. So `Method` accepts exactly this pair , but carries out no work (Q: why can we not add `Method` inside `BaseSensor`?). Then we create the `runSingle()` to accept `Method` arguments, so as to grab this pair I referred to before. (Q2: I assume `this` points to a `BaseSensor` instance. How do I get to execute the Mixin::method ??) – nass Sep 22 '16 at 22:33
  • Unfortunately we can't really use this approach when virtual dispatch comes into play as there is no way to declare a scope of the method using `->*` operator see e.g. [my question from yesterday](http://stackoverflow.com/questions/39626901/why-cant-one-use-scope-resolution-with-member-pointer-dereference) – W.F. Sep 22 '16 at 22:34
  • Hi I had not seen your replies, until after I completed writting my interpretation. please discard it. The important parts are 2 now. 1). how does `this` point to the instance of a `SensorType` mixin and not to the `BaseSensor` (so as to run the `SensorType::update()` and not the `BaseSensor::update()`? . 2) I do not understand the `Ts{}` within `runSingle()` call. What do the `{}` stand for? – nass Sep 22 '16 at 22:38
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/123992/discussion-between-nass-and-w-f). – nass Sep 22 '16 at 22:41
  • actually `BaseSensor` is also the `SensorType` all we need to do is interpret it this way -- to achieve that we can simply static_cast it **but** actually we do not have to as we pass exact pointer of the member function we would like to call – W.F. Sep 22 '16 at 22:41
  • Hi may I point you to this question in case you have some idea? http://stackoverflow.com/questions/39650751/variadic-templates-with-template-function-names-and-passing-arguments-and-return/39651347#39651347 – nass Sep 23 '16 at 14:32