多态性、可变参数模板继承、切片、boost::any 类型转换
Polymorphism, variadic template inheritance, slicing, boost::any type cast
此程序编译,但 boost::any 转换失败。我怀疑以这种方式切片模板 class 会混淆指针算法。这个想法是容器中存储的内容
std::vector<boost::any> pressures;
是不同的类型,例如
Pressure<Printer>, or Pressure<Printer, Printer> etc.
由于我通过将类型存储在 boost::any 中而丢失了类型,因此我需要调用 Change 而不必知道给定压力下观察者的实际数量。我尝试通过多态和虚方法来解决,但至少这种尝试是行不通的。
有什么建议吗?
#include <utility>
#include <tuple>
#include <iostream>
enum class EventType {UNKNOWN};
// Note: All Observers must implement OnNotify for any subject types they wish to observe
// Any unimplemented subject types that are used will result in a compiler error
template <typename Base> class Observer
{
public:
Observer() : obsID_(obsIDTracker_++) {}
template <typename T> void OnNotifyImpl(T &subject, EventType event)
{
static_cast<Base *>(this)->OnNotify(subject, event);
}
int GetID() const
{
return obsID_;
}
private:
int obsID_;
static int obsIDTracker_;
};
template <typename base> int Observer<base>::obsIDTracker_ = 0;
// Recursive helper structs for implementing calls to all observers held within subjects
template <int N, typename T, typename... Args> struct NotifyHelper
{
static void NotifyImpl(T &subject, EventType event,
std::tuple<Args...> &obs)
{
std::get<sizeof...(Args) - N>(obs).OnNotifyImpl(subject, event);
NotifyHelper<N - 1, T, Args...>::NotifyImpl(subject, event, obs);
}
};
template <typename T, typename... Args> struct NotifyHelper<0, T, Args...>
{
static void NotifyImpl(T &subject, EventType event,
std::tuple<Args...> &obs) {}
};
// See MakeSubject function for instance usage
template <typename T, typename... Obs> class Subject
{
public:
static const int NumberOfObservers = sizeof...(Obs);
Subject(std::tuple<Obs &...> &&obs) : observers(obs) {}
void NotifyAll(EventType event)
{
NotifyHelper<NumberOfObservers, T, Obs &...>::NotifyImpl(
*static_cast<T *>(this), event, observers);
}
private:
std::tuple<Obs &...> observers;
};
class PressureInterface
{
public:
virtual ~PressureInterface() {}
virtual void Change(int value) {}
};
// CRTP Abstract Base class for implementing static subject.
// Example Subclass Usage -- Pressure Sensor:
template <typename... Obs>
class Pressure : public PressureInterface, public Subject<Pressure<Obs...>, Obs...>
{
public:
typedef Subject<Pressure<Obs...>, Obs...> BaseType;
Pressure(std::tuple<Obs &...> &&observers, int pressure)
: BaseType(std::move(observers)), pressure_(pressure) {}
virtual void Change(int value)
{
pressure_ = value;
this->NotifyAll(EventType::UNKNOWN);
}
int GetPressure() const
{
return pressure_;
}
private:
int pressure_;
};
// CRTP Abstract Base class for implementing static subject.
// Example Subclass Usage -- Printing Observer:
class Printer : public Observer<Printer>
{
public:
Printer() : timesTriggered_(0) {}
template <typename... Args>
void OnNotify(Pressure<Args...> &subject, EventType event)
{
std::cout << "Observer ID: " << this->GetID() << std::endl;
switch (event)
{
case EventType::UNKNOWN:
{
std::cout << "Unknown Event -- Event #" << timesTriggered_++
<< std::endl;
std::cout << "Pressure: " << subject.GetPressure() << std::endl;
break;
}
default:
{
break;
}
}
}
private:
int timesTriggered_;
};
// Binding function for use with MakeSubject
// Arguments: observer objects to observe subject notifications
// Return: tuple of references to observers
template <typename... Obs> std::tuple<Obs &...> BindObservers(Obs &... obs)
{
return std::tuple<Obs &...>(obs...);
}
// Creator to ease subject creation
// Template Arguments: Subject subclass type
// Arguments: Result from BindObservers
// Any constructor arguments for Subject subclass
// Return: Subject subclass
// Example Usage:
// auto pressure = MakeSubject<Pressure>(BindObservers(printerObs), initialPressure);
template <template <typename...> class T, typename... Args, typename... Obs>
T<Obs...> MakeSubject(std::tuple<Obs &...> &&obs, Args &&... args)
{
return T<Obs...>(std::move(obs), args...);
}
#include <boost/any.hpp>
int main()
{
std::vector<boost::any> pressures;
Printer printerObs1;
Printer printerObs2;
const int initialPressure = 1;
auto pressure = MakeSubject<Pressure>(
BindObservers(printerObs1, printerObs2), initialPressure);
pressures.push_back(pressure);
pressure.Change(12);
decltype(pressure) *p = boost::any_cast<decltype(pressure)>(&pressures[0]);
p->Change(1999);
PressureInterface *qip = boost::any_cast<PressureInterface>(&pressures[0]); //This cast returns nullptr
std::cout << "The cast works\n";
if(nullptr != qip)
qip->Change(2001);
}
编辑
我第一次尝试存储 Change 函数的地址:
std::vector<std::function<boost::any *>> pressures;
如何push_back函数的地址?这不起作用:
pressures.push_back(std::function<decltype(&pressure.Change>);
/home/idf/Documents/OrigObserverExam/ObserverExample.cpp|157|error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function. Say '&Pressure<Printer, Printer>::Change' [-fpermissive]|
然后我该如何提取它?
std::function<void(int)> *qip = boost::any_cast<std::function<void(int)>*>(&(pressures[0].Change));
std::cout << "The cast works\n";
if(nullptr != qip)
*qip(2001);
编辑 2
当我添加建议的代码时,出现错误:
/home/idf/Documents/OrigObserverExam/ObserverExample.cpp|167|error: 'decay_t' is not a member of 'std'|
#include <type_traits>
#include <boost/any.hpp>
struct changable {
boost::any data;
using do_change = void(*)(boost::any*, int);
do_change f = nullptr;
void change(int x) {
if (f) f(&data, x);
}
template<class T>
static do_change type_erase_change() {
return [](boost::any* a, int x){
T* t = boost::any_cast<T>(a);
if (t) t->Change(x);
};
}
template<class T>
changable( T&& t ):
data(std::forward<T>(t)),
f( type_erase_change<std::decay_t<T>>() )
{}
changable(changable const&)=default;
changable(changable &&)=default;
changable()=default;
};
编辑 3 安装 C++14:
如何使用这个结构?我可以说:
std::vector<changable> pressures;
而且我能够push_back压力
pressures.push_back(pressure);
但是,我不确定如何调用 pressures[0].Change(1999)。如果我说我收到错误:
pressures[0].Change(2000);
ObserverExample.cpp|199|error: '__gnu_cxx::__alloc_traits<std::allocator<changable> >::value_type' has no member named 'Change'
boost::any
允许您将类型转换回与您输入的类型完全相同的类型。不是父类型,是相同的类型。
如果您想键入擦除调用方法,请尝试 std::function<void()>
或 std::function<void(boost::any*)>
。
这是捆绑在一起的 change(int)
和 boost::any
类型的橡皮擦:
struct changable {
boost::any data;
using do_change = void(*)(boost::any*, int);
do_change f = nullptr;
void change(int x) {
if (f) f(&data, x);
}
template<class T>
static do_change type_erase_change() {
return [](boost::any* a, int x){
T* t = boost::any_cast<T>(a);
if (t) t->Change(x);
};
}
template<class T>
changable( T&& t ):
data(std::forward<T>(t)),
f( type_erase_change<std::decay_t<T>>() )
{}
changable(changable const&)=default;
changable(changable &&)=default;
changable()=default;
};
不需要公开 Change
的接口 class。只要传递给上述类型擦除器的类型具有 Change(int)
方法,一切都很好。
此程序编译,但 boost::any 转换失败。我怀疑以这种方式切片模板 class 会混淆指针算法。这个想法是容器中存储的内容
std::vector<boost::any> pressures;
是不同的类型,例如
Pressure<Printer>, or Pressure<Printer, Printer> etc.
由于我通过将类型存储在 boost::any 中而丢失了类型,因此我需要调用 Change 而不必知道给定压力下观察者的实际数量。我尝试通过多态和虚方法来解决,但至少这种尝试是行不通的。
有什么建议吗?
#include <utility>
#include <tuple>
#include <iostream>
enum class EventType {UNKNOWN};
// Note: All Observers must implement OnNotify for any subject types they wish to observe
// Any unimplemented subject types that are used will result in a compiler error
template <typename Base> class Observer
{
public:
Observer() : obsID_(obsIDTracker_++) {}
template <typename T> void OnNotifyImpl(T &subject, EventType event)
{
static_cast<Base *>(this)->OnNotify(subject, event);
}
int GetID() const
{
return obsID_;
}
private:
int obsID_;
static int obsIDTracker_;
};
template <typename base> int Observer<base>::obsIDTracker_ = 0;
// Recursive helper structs for implementing calls to all observers held within subjects
template <int N, typename T, typename... Args> struct NotifyHelper
{
static void NotifyImpl(T &subject, EventType event,
std::tuple<Args...> &obs)
{
std::get<sizeof...(Args) - N>(obs).OnNotifyImpl(subject, event);
NotifyHelper<N - 1, T, Args...>::NotifyImpl(subject, event, obs);
}
};
template <typename T, typename... Args> struct NotifyHelper<0, T, Args...>
{
static void NotifyImpl(T &subject, EventType event,
std::tuple<Args...> &obs) {}
};
// See MakeSubject function for instance usage
template <typename T, typename... Obs> class Subject
{
public:
static const int NumberOfObservers = sizeof...(Obs);
Subject(std::tuple<Obs &...> &&obs) : observers(obs) {}
void NotifyAll(EventType event)
{
NotifyHelper<NumberOfObservers, T, Obs &...>::NotifyImpl(
*static_cast<T *>(this), event, observers);
}
private:
std::tuple<Obs &...> observers;
};
class PressureInterface
{
public:
virtual ~PressureInterface() {}
virtual void Change(int value) {}
};
// CRTP Abstract Base class for implementing static subject.
// Example Subclass Usage -- Pressure Sensor:
template <typename... Obs>
class Pressure : public PressureInterface, public Subject<Pressure<Obs...>, Obs...>
{
public:
typedef Subject<Pressure<Obs...>, Obs...> BaseType;
Pressure(std::tuple<Obs &...> &&observers, int pressure)
: BaseType(std::move(observers)), pressure_(pressure) {}
virtual void Change(int value)
{
pressure_ = value;
this->NotifyAll(EventType::UNKNOWN);
}
int GetPressure() const
{
return pressure_;
}
private:
int pressure_;
};
// CRTP Abstract Base class for implementing static subject.
// Example Subclass Usage -- Printing Observer:
class Printer : public Observer<Printer>
{
public:
Printer() : timesTriggered_(0) {}
template <typename... Args>
void OnNotify(Pressure<Args...> &subject, EventType event)
{
std::cout << "Observer ID: " << this->GetID() << std::endl;
switch (event)
{
case EventType::UNKNOWN:
{
std::cout << "Unknown Event -- Event #" << timesTriggered_++
<< std::endl;
std::cout << "Pressure: " << subject.GetPressure() << std::endl;
break;
}
default:
{
break;
}
}
}
private:
int timesTriggered_;
};
// Binding function for use with MakeSubject
// Arguments: observer objects to observe subject notifications
// Return: tuple of references to observers
template <typename... Obs> std::tuple<Obs &...> BindObservers(Obs &... obs)
{
return std::tuple<Obs &...>(obs...);
}
// Creator to ease subject creation
// Template Arguments: Subject subclass type
// Arguments: Result from BindObservers
// Any constructor arguments for Subject subclass
// Return: Subject subclass
// Example Usage:
// auto pressure = MakeSubject<Pressure>(BindObservers(printerObs), initialPressure);
template <template <typename...> class T, typename... Args, typename... Obs>
T<Obs...> MakeSubject(std::tuple<Obs &...> &&obs, Args &&... args)
{
return T<Obs...>(std::move(obs), args...);
}
#include <boost/any.hpp>
int main()
{
std::vector<boost::any> pressures;
Printer printerObs1;
Printer printerObs2;
const int initialPressure = 1;
auto pressure = MakeSubject<Pressure>(
BindObservers(printerObs1, printerObs2), initialPressure);
pressures.push_back(pressure);
pressure.Change(12);
decltype(pressure) *p = boost::any_cast<decltype(pressure)>(&pressures[0]);
p->Change(1999);
PressureInterface *qip = boost::any_cast<PressureInterface>(&pressures[0]); //This cast returns nullptr
std::cout << "The cast works\n";
if(nullptr != qip)
qip->Change(2001);
}
编辑
我第一次尝试存储 Change 函数的地址:
std::vector<std::function<boost::any *>> pressures;
如何push_back函数的地址?这不起作用:
pressures.push_back(std::function<decltype(&pressure.Change>);
/home/idf/Documents/OrigObserverExam/ObserverExample.cpp|157|error: ISO C++ forbids taking the address of a bound member function to form a pointer to member function. Say '&Pressure<Printer, Printer>::Change' [-fpermissive]|
然后我该如何提取它?
std::function<void(int)> *qip = boost::any_cast<std::function<void(int)>*>(&(pressures[0].Change));
std::cout << "The cast works\n";
if(nullptr != qip)
*qip(2001);
编辑 2
当我添加建议的代码时,出现错误:
/home/idf/Documents/OrigObserverExam/ObserverExample.cpp|167|error: 'decay_t' is not a member of 'std'|
#include <type_traits>
#include <boost/any.hpp>
struct changable {
boost::any data;
using do_change = void(*)(boost::any*, int);
do_change f = nullptr;
void change(int x) {
if (f) f(&data, x);
}
template<class T>
static do_change type_erase_change() {
return [](boost::any* a, int x){
T* t = boost::any_cast<T>(a);
if (t) t->Change(x);
};
}
template<class T>
changable( T&& t ):
data(std::forward<T>(t)),
f( type_erase_change<std::decay_t<T>>() )
{}
changable(changable const&)=default;
changable(changable &&)=default;
changable()=default;
};
编辑 3 安装 C++14:
如何使用这个结构?我可以说:
std::vector<changable> pressures;
而且我能够push_back压力
pressures.push_back(pressure);
但是,我不确定如何调用 pressures[0].Change(1999)。如果我说我收到错误:
pressures[0].Change(2000);
ObserverExample.cpp|199|error: '__gnu_cxx::__alloc_traits<std::allocator<changable> >::value_type' has no member named 'Change'
boost::any
允许您将类型转换回与您输入的类型完全相同的类型。不是父类型,是相同的类型。
如果您想键入擦除调用方法,请尝试 std::function<void()>
或 std::function<void(boost::any*)>
。
这是捆绑在一起的 change(int)
和 boost::any
类型的橡皮擦:
struct changable {
boost::any data;
using do_change = void(*)(boost::any*, int);
do_change f = nullptr;
void change(int x) {
if (f) f(&data, x);
}
template<class T>
static do_change type_erase_change() {
return [](boost::any* a, int x){
T* t = boost::any_cast<T>(a);
if (t) t->Change(x);
};
}
template<class T>
changable( T&& t ):
data(std::forward<T>(t)),
f( type_erase_change<std::decay_t<T>>() )
{}
changable(changable const&)=default;
changable(changable &&)=default;
changable()=default;
};
不需要公开 Change
的接口 class。只要传递给上述类型擦除器的类型具有 Change(int)
方法,一切都很好。