2016-09-23 3 views
3

Следующее из this вопроса, я пытался создать функцию шаблона, которая вызывает все однотипные методы его микшинов. Это было сделано и проверено в предыдущем вопросе.variadic templates с именами функций шаблона и передающими аргументами и возвращаемыми значениями около

Теперь я пытаюсь получить возвращаемое значение SensorType ::

Аналитически:

#include<iostream> 
#include <string> 

struct EdgeSensor 
{ 
    void update(int i) { std::cout << "EdgeSensor::update " << i << std::endl; } 
    void updat2(const int i) { std::cout << "EdgeSensor::updat2" << i << std::endl; } 
    std::string printStats() { std::cout << "EdgeSensor::printStats" << std::endl; 
           return std::string("EdgeSensor::printStats"); } 
}; 

struct TrendSensor 
{ 
    void update(int i) { std::cout << "TrendSensor::update" << i << std::endl; } 
    void updat2(const int i) { std::cout << "TrendSensor::updat2" << i << std::endl; } 
    std::string printStats() { std::cout << "TrendSensor::printStats" << std::endl; 
           return std::string("TrendSensor::printStats"); } 
}; 

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

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

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

public: 
    void update() { 
     runAll<Method<SensorType, &SensorType::update>...>(2); 
    } 
    void updat2() { 
     const int k = 3; 
     runAll<Method<SensorType, &SensorType::updat2>...>(k); 
    } 
    void printStats() { 
    // runAll<Method<SensorType, &SensorType::printStats>...>(); 
    } 
}; 

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

    { 
    BaseSensor<EdgeSensor> ets; 
    ets.update(); 
    ets.updat2(); 
    ets.printStats(); 
    } 
} 

Вышеизложенные компилирует и работает нормально. Проблема в следующем: как я могу собрать возвращаемые значения (std :: strings) из запуска метода mixin SensorType::printStats() в BaseSensor::printStats()?

Если я попытаюсь создать вторую версию функций run* и шаблон Method, я не могу скомпилировать его. Скажите, что я сделал:

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

template <class T, void(T::*M)()> 
int runSingle2(Method2<T, M>) { 
    (this->*M)(); 
    return 0; 
} 

template <class... Ts> 
void runAll2() { 
    std::string s; 
    int run[sizeof...(Ts)] = { s = runSingle2(Ts{})... }; 
    (void)run; 
    std::cout << "s=" << s << std::endl; 
} 

public: 
    void update() { 
     int k = 4; 
     runAll<Method<SensorType, &SensorType::update>...>(k); 
    } 
    void printStats() { 
     runAll2<Method2<SensorType, &SensorType::printStats>...>(); 
    } 
}; 

Это не компилируется говоря

g++ -Wall -Wextra -g -std=c++11 -c -o "obj_dbg/main.opp" "main.cpp" 
main.cpp: In instantiation of ‘void BaseSensor<SensorType>::printStats() [with SensorType = EdgeSensor, TrendSensor]’: 
main.cpp:65:20: required from here 
main.cpp:58:8: error: could not convert template argument ‘&EdgeSensor::printStats’ to ‘void (EdgeSensor::*)()’ 
make: *** [obj_dbg/main.opp] Error 1 

Так как я могу захватить возвращаемые значения из SensorType::printStats()?

ответ

2

Не уверен, что вы можете использовать C++ 11, если да, то я думаю, что это самый простой?

#include <iostream> 
#include <string> 

struct EdgeSensor 
{ 
    void update(int i) { std::cout << "EdgeSensor::update " << i << std::endl; } 
    void updat2(const int i) { std::cout << "EdgeSensor::updat2" << i << std::endl; } 
    std::string printStats() { std::cout << "EdgeSensor::printStats" << std::endl; 
           return std::string("EdgeSensor::printStats"); } 
}; 

struct TrendSensor 
{ 
    void update(int i) { std::cout << "TrendSensor::update" << i << std::endl; } 
    void updat2(const int i) { std::cout << "TrendSensor::updat2" << i << std::endl; } 
    std::string printStats() { std::cout << "TrendSensor::printStats" << std::endl; 
           return std::string("TrendSensor::printStats"); } 
}; 

template<typename ... SensorType> 
class BaseSensor : public SensorType ... //to my BaseSensor class 
{ 
public: 
    void update() { 
     auto v = { (static_cast<SensorType*>(this)->update(1), 0)... }; // * 
     (void) v; 
    } 
    void updat2() { 
     const int k = 3; 
     auto v = { (static_cast<SensorType*>(this)->updat2(k), 0)... }; // * 
     (void) v; 
    } 
    void printStats() { 
     auto v = { static_cast<SensorType*>(this)->printStats()... }; 
     for (auto s : v) { 
      std::cout << s << std::endl; 
     } 
    } 
}; 

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

    { 
    BaseSensor<EdgeSensor> ets; 
    ets.update(); 
    ets.updat2(); 
    ets.printStats(); 
    } 
} 
  • Примечание: Я использую GCC расширение здесь, но я думаю, что вы используете GCC, поэтому он должен быть хорошо
+0

Ницца !! Расширение gcc правильно выводит тип массива? –

+1

'auto v = {(this-> SensorType :: update (1), 0) ...};' может быть даже приятнее (позволяет избежать возможной ошибки виртуальной отправки, что невозможно сделать в подходе указателя участника) –

+1

@WF нет, это часть стандарта, однако в этом нет необходимости использовать оператор ',', это расширение GCC означает выражение выражения перед ',', отбрасывать результат, а затем применять выражение после ', ' 'и использовать его значение. Следовательно, у вас есть список инициализаторов с N 0s, – Nim

2

Вот вы пишете обзор так, как он работает в соответствии с просьбой:

#include<iostream> 
#include <string> 
#include <vector> 

struct EdgeSensor 
{ 
    void update(int i) { std::cout << "EdgeSensor::update " << i << std::endl; } 
    void updat2(const int i) { std::cout << "EdgeSensor::updat2" << i << std::endl; } 
    std::string printStats() { std::cout << "EdgeSensor::printStats" << std::endl; 
    return std::string("EdgeSensor::printStats"); } 
}; 

struct TrendSensor 
{ 
    void update(int i) { std::cout << "TrendSensor::update" << i << std::endl; } 
    void updat2(const int i) { std::cout << "TrendSensor::updat2" << i << std::endl; } 
    std::string printStats() { std::cout << "TrendSensor::printStats" << std::endl; 
    return std::string("TrendSensor::printStats"); } 
}; 

template<typename ... SensorType> 
class BaseSensor : public SensorType ... { 
    template<typename F> 
    struct Invoke; 

    template<typename R, typename... A> 
    struct Invoke<R(A...)> { 
     template <R(SensorType::* ...M)(A...), typename T> 
     static std::vector<R> run(T *t, A... args) { 
      std::vector<R> vec; 
      int arr[] = { (vec.push_back((t->*M)(args...)), 0)... }; 
      (void)arr; 
      return vec; 
     } 
    }; 

    template<typename... A> 
    struct Invoke<void(A...)> { 
     template <void(SensorType::* ...M)(A...), typename T> 
     static void run(T *t, A... args) { 
      int arr[] = { ((t->*M)(args...), 0)... }; 
      (void)arr; 
     } 
    }; 

public: 
    void update() { 
     Invoke<void(int)>::template run<&SensorType::update...>(this, 2); 
    } 
    void updat2() { 
     const int k = 3; 
     Invoke<void(int)>::template run<&SensorType::updat2...>(this, k); 
    } 
    void printStats() { 
     auto vec = Invoke<std::string(void)>::template run<&SensorType::printStats...>(this); 
     for(auto &&v: vec) { 
      std::cout << "--->" << v << std::endl; 
     } 
    } 
}; 

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

    { 
    BaseSensor<EdgeSensor> ets; 
    ets.update(); 
    ets.updat2(); 
    ets.printStats(); 
    } 
} 

Я переработан немного коды, что не было никакой необходимости в Method класса. Это работает по назначению, и строки, возвращаемые методами printStats, теперь собираются в std::vector и возвращаются вызывающему.

+0

Привет там, к сожалению, выше не компилирует https: // godbolt.org/g/tJMtiZ. Я пытаюсь понять, почему, но, возможно, вы можете легко идентифицировать его. – nass

+0

@nass. Он компилируется с помощью [clang] (https://godbolt.org/g/Nm8maE), но не с GCC. Я создал [вопрос] (http://stackoverflow.com/questions/39665300/gcc-vs-clang-variadic-template-and-pointers-to-member-methods), чтобы выяснить, является ли это проблемой GCC или кода, но ответов пока нет. Я вернусь, пока у меня есть новости для вас. – skypjack

+0

ах да, действительно, я видел это и следил за ним :) Возможно, было бы полезно добавить эту информацию в качестве части ответа (по крайней мере, до тех пор, пока не будет предоставлено решение) – nass

2

Чтобы расширить решение любой функции-члена, которую вы могли бы сделать (и на самом деле немного упростите это, имея в виду ограничение C++ 11). Подход разрешает тип функции-члена, чтобы иметь возможность выводить его тип результата. Он также использует InferOwnerType для вывода типа мезинина и избегает прямого прохождения статически отлитого указателя this. В зависимости от результата функции-члена теперь мы можем сохранить его в массиве или использовать трюк с массивом int, чтобы убедиться, что вызывается каждая функция-член.

#include <iostream> // std::cout std::endl 
#include <string> // std::string 
#include <utility> // std::declval 

struct EdgeSensor //a mixin 
{ 
    void update(int a){ std::cout << "EdgeSensor::update" << a << std::endl; } 
    std::string updat2(int const v) { return "EdgeSensor::printStats"; } 
}; 

struct TrendSensor //another mixin 
{ 
    void update(int a){ std::cout << "TrendSensor::update" << std::endl; } 
    std::string updat2(int const v) { return "TrendSensor::printStats"; } 
}; 

template <class Res, class This, class... Args> 
This InferOwnerType(Res (This::*foo)(Args...)) { } 

template<typename ... SensorType> 
class BaseSensor : public SensorType ... //to my BaseSensor class 
{ 
    template <class M, class... Args> 
    auto run(M m, Args... args) 
     -> decltype((std::declval<decltype(InferOwnerType(m))*>()->*m)(args...)) { 
     return (static_cast<decltype(InferOwnerType(m))*>(this)->*m)(args...); 
    } 

public: 
    template <class... Args> 
    void update(Args... args) { 
     int arr[] = {(run(&SensorType::update, args...), 0)...}; 
     (void)arr; 
    } 
    template <class... Args> 
    void updat2(Args... args) { 
     std::string s[] = {run(&SensorType::updat2, args...)...}; 
     for (int i = 0; i < sizeof...(SensorType); i++) 
      std::cout << s[i] << std::endl; 
    } 
}; 

int main() { 
    BaseSensor<EdgeSensor, TrendSensor> bs; 
    bs.update(4); 
    bs.updat2(0); 
    BaseSensor<EdgeSensor> bs2; 
    bs2.update(1); 
    bs2.updat2(0); 
} 
+0

Привет, еще раз, пытаясь понять, что происходит, у меня есть несколько вопросов :) мы можем поговорить о них в окне чата? – nass

+0

http://chat.stackoverflow.com/rooms/123992/discussion-between-nass-and-w-f –

+1

«InferThisType» как-то сумасшедший, но интригующий тоже. :-D – skypjack

Смежные вопросы