需求点:假设一个第三方库有提供了很多Msg类,这些Msg类都提供了固定的一个成员函数,但是却没有虚基函数。如何在自己的项目代码中更好的使用这些Msg类内?
一、使用std::variant和std::visit
#include <iostream>
#include <memory> // for std::unique_ptr and std::make_unique
#include <string>
#include <utility> // for std::move
#include <variant>
#include <vector>
// 外部库 Start
struct MoveMsg {
int x;
int y;
void speak() { std::cout << "Move " << x << ", " << y << '\n'; }
};
struct JumpMsg {
int height;
void speak() { std::cout << "Jump " << height << '\n'; }
};
struct SleepMsg {
int time;
void speak() { std::cout << "Sleep " << time << '\n'; }
};
struct ExitMsg {
void speak() { std::cout << "Exit" << '\n'; }
};
// 外部库 End
int main() {
using Msg = std::variant<MoveMsg, JumpMsg, SleepMsg, ExitMsg>;
std::vector<Msg> msgs;
msgs.push_back(MoveMsg{1, 2});
msgs.push_back(JumpMsg{1});
for (auto&& msg : msgs) {
std::visit([](auto& msg) { msg.speak(); }, msg);
}
return 0;
}
二、使用虚函数
C++20语法
#include <iostream>
#include <memory> // for std::unique_ptr and std::make_unique
#include <string>
#include <utility> // for std::move
#include <variant>
#include <vector>
// 外部库 Start
struct MoveMsg {
int x;
int y;
void speak() { std::cout << "Move " << x << ", " << y << '\n'; }
};
struct JumpMsg {
int height;
void speak() { std::cout << "Jump " << height << '\n'; }
void happy() { std::cout << "happy " << height << '\n'; }
};
struct SleepMsg {
int time;
void speak() { std::cout << "Sleep " << time << '\n'; }
};
struct ExitMsg {
void speak() { std::cout << "Exit" << '\n'; }
};
// 外部库 End
struct MsgBase {
virtual void speak() = 0;
virtual void happy() = 0;
virtual std::shared_ptr<MsgBase> clone() const = 0;
virtual ~MsgBase() = default;
};
//内部代码使用MsgBase去包装库的speak()和happy()函数
template <class Msg>
struct MsgImpl : MsgBase {
Msg msg;
template <class ...Ts>
MsgImpl(Ts &&...ts) : msg{std::forward<Ts>(ts)...} {
}
void speak() override {
msg.speak();
}
void happy() override {
if constexpr (requires {msg.happy();} )
{
msg.happy();
}
else
{
std::cout<< "no happy\n";
}
}
std::shared_ptr<MsgBase> clone() const override {
return std::make_shared<MsgImpl<Msg>>(msg);
}
};
template <class Msg, class ...Ts>
std::shared_ptr<MsgBase> makeMsg(Ts &&...ts) {
return std::make_shared<MsgImpl<Msg>>(std::forward<Ts>(ts)...);
}
int main() {
std::vector<std::shared_ptr<MsgBase>> msgs;
msgs.push_back(makeMsg<MoveMsg>(1, 2));
msgs.push_back(makeMsg<JumpMsg>(1));
for (auto&& msg : msgs) {
msg->speak();
msg->happy();
}
return 0;
}
C++14写法1
#include <iostream>
#include <memory> // for std::unique_ptr and std::make_unique
#include <string>
#include <utility> // for std::move
#include <variant>
#include <vector>
// 外部库 Start
struct MoveMsg {
int x;
int y;
void speak() { std::cout << "Move " << x << ", " << y << '\n'; }
};
struct JumpMsg {
int height;
void speak() { std::cout << "Jump " << height << '\n'; }
void happy() { std::cout << "happy " << height << '\n'; }
};
struct SleepMsg {
int time;
void speak() { std::cout << "Sleep " << time << '\n'; }
};
struct ExitMsg {
void speak() { std::cout << "Exit" << '\n'; }
};
// 外部库 End
struct MsgBase {
virtual void speak() = 0;
virtual void happy() = 0;
virtual std::shared_ptr<MsgBase> clone() const = 0;
virtual ~MsgBase() = default;
};
// 特质,用于检测 happy 方法是否存在
template<typename T>
struct has_happy {
private:
typedef char YesType[1];
typedef char NoType[2];
template <typename C> static YesType& test(decltype(&C::happy));
template <typename C> static NoType& test(...);
public:
enum { value = sizeof(test<T>(0)) == sizeof(YesType) };
};
// 函数重载用于调用 happy 或者输出 "no happy"
template <typename T>
typename std::enable_if<has_happy<T>::value>::type call_happy(T& t) {
t.happy();
}
template <typename T>
typename std::enable_if<!has_happy<T>::value>::type call_happy(T&) {
std::cout << "no happy\n";
}
// MsgImpl 用于包装库的消息类型
template <class Msg>
struct MsgImpl : MsgBase {
Msg msg;
template <class ...Ts>
MsgImpl(Ts &&...ts) : msg{std::forward<Ts>(ts)...} {}
void speak() override {
msg.speak();
}
void happy() override {
call_happy(msg);
}
std::shared_ptr<MsgBase> clone() const override {
return std::make_shared<MsgImpl<Msg>>(msg);
}
};
template <class Msg, class ...Ts>
std::shared_ptr<MsgBase> makeMsg(Ts &&...ts) {
return std::make_shared<MsgImpl<Msg>>(std::forward<Ts>(ts)...);
}
int main() {
std::vector<std::shared_ptr<MsgBase>> msgs;
msgs.push_back(makeMsg<MoveMsg>(1, 2));
msgs.push_back(makeMsg<JumpMsg>(1));
for (auto&& msg : msgs) {
msg->speak();
msg->happy();
}
return 0;
}
has_happy 模板:
- has_happy 模板用于检测 Msg 类中是否有 happy 方法。
- SFINAE 技术用于检查特定方法是否存在。
- test 函数用于确定 happy 方法的存在性,如果存在,返回 sizeof(char),否则返回 sizeof(int)。
call_happy 函数:
- 根据 std::integral_constant 的值,选择调用 happy 方法或输出 “no happy”。
- std::true_type 和 std::false_type 是 std::integral_constant 的特例化,分别表示 true 和 false。
C++14写法2:
#include <iostream>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include <type_traits>
// 外部库 Start
struct MoveMsg {
int x;
int y;
void speak() { std::cout << "Move " << x << ", " << y << '\n'; }
};
struct JumpMsg {
int height;
void speak() { std::cout << "Jump " << height << '\n'; }
void happy() { std::cout << "happy " << height << '\n'; }
};
struct SleepMsg {
int time;
void speak() { std::cout << "Sleep " << time << '\n'; }
};
struct ExitMsg {
void speak() { std::cout << "Exit" << '\n'; }
};
// 外部库 End
struct MsgBase {
virtual void speak() = 0;
virtual void happy() = 0;
virtual std::shared_ptr<MsgBase> clone() const = 0;
virtual ~MsgBase() = default;
};
// 检测 happy 方法存在性的模板
template<typename T>
struct has_happy {
// 利用 SFINAE 技术检测 happy 方法
template<typename U>
static auto test(int) -> decltype(std::declval<U>().happy(), std::true_type{});
template<typename>
static std::false_type test(...);
static constexpr bool value = decltype(test<T>(0))::value;
};
// MsgImpl 包装库的消息类型
template <class Msg>
struct MsgImpl : MsgBase {
Msg msg;
template <class ...Ts>
MsgImpl(Ts&&... ts) : msg{std::forward<Ts>(ts)...} {}
void speak() override {
msg.speak();
}
void happy() override {
call_happy(msg, std::integral_constant<bool, has_happy<Msg>::value>{});
}
std::shared_ptr<MsgBase> clone() const override {
return std::make_shared<MsgImpl<Msg>>(msg);
}
private:
// 当 Msg 有 happy 方法时调用
template<typename T>
void call_happy(T& t, std::true_type) {
t.happy();
}
// 当 Msg 没有 happy 方法时调用
template<typename T>
void call_happy(T&, std::false_type) {
std::cout << "no happy\n";
}
};
template <class Msg, class ...Ts>
std::shared_ptr<MsgBase> makeMsg(Ts&&... ts) {
return std::make_shared<MsgImpl<Msg>>(std::forward<Ts>(ts)...);
}
int main() {
std::vector<std::shared_ptr<MsgBase>> msgs;
msgs.push_back(makeMsg<MoveMsg>(1, 2));
msgs.push_back(makeMsg<JumpMsg>(1));
for (auto&& msg : msgs) {
msg->speak();
msg->happy();
}
return 0;
}
使用特质 has_happy 来检测 happy 方法是否存在。
使用 enable_if 和函数重载来根据特质的值调用不同的实现。
06:36