You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何简化C++20中包装成员函数的std::map实现?

方案一:修改AnyCallable以支持成员函数指针

AnyCallable的核心是通过类型擦除统一不同签名的可调用对象接口。要直接支持成员函数指针,关键是处理成员函数依赖的this指针——我们可以在构造时绑定Pipe实例,把成员函数转换为普通可调用对象。

步骤与代码实现

  1. 先定义示例Pipe类:
#include <any>
#include <vector>
#include <map>
#include <memory>
#include <string>
#include <iostream>
#include <tuple>
#include <stdexcept>
#include <utility>

class Pipe {
public:
    int Add(int a, int b) { return a + b; }
    std::string Greet(const std::string& name) { return "Hello, " + name; }
    void Print(const std::string& msg) { std::cout << msg << std::endl; }
};
  1. 扩展原有AnyCallable,添加成员函数指针的构造重载:
namespace detail {
    // 辅助函数:解析std::any参数并调用普通函数
    template<typename Func, size_t... Idx, typename ArgsTuple>
    std::any InvokeHelper(Func&& func, std::index_sequence<Idx...>, ArgsTuple&& args) {
        using RetType = decltype(func(std::get<Idx>(std::forward<ArgsTuple>(args))...));
        if constexpr (std::is_void_v<RetType>) {
            func(std::get<Idx>(std::forward<ArgsTuple>(args))...);
            return std::any();
        } else {
            return std::any(func(std::get<Idx>(std::forward<ArgsTuple>(args))...));
        }
    }

    template<typename Func, typename... Args>
    std::any InvokeFunc(Func&& func, std::vector<std::any> args) {
        if (args.size() != sizeof...(Args)) {
            throw std::invalid_argument("Argument count mismatch");
        }
        // 将std::vector<std::any>转换为对应类型的tuple
        std::tuple<std::decay_t<Args>...> args_tuple;
        std::apply([&](auto&... elem) {
            size_t idx = 0;
            ((elem = std::any_cast<std::decay_t<Args>>(args[idx++])), ...);
        }, args_tuple);
        return InvokeHelper(std::forward<Func>(func), std::index_sequence_for<Args...>{}, std::move(args_tuple));
    }
}

class AnyCallable {
public:
    // 原有构造函数:支持普通可调用对象
    template<typename Func>
    AnyCallable(Func&& func) : impl_(std::make_unique<Impl<Func>>(std::forward<Func>(func))) {}

    // 新增构造函数:直接接受Pipe成员函数指针和实例
    template<typename Ret, typename... Args>
    AnyCallable(Ret (Pipe::*mem_func)(Args...), Pipe* instance)
        : impl_(std::make_unique<Impl<std::function<Ret(Args...)>>>(
            [mem_func, instance](Args... args) {
                return (instance->*mem_func)(std::forward<Args>(args)...);
            }
        )) {}

    // 统一调用接口
    std::any operator()(std::vector<std::any> args) {
        return impl_->Invoke(std::move(args));
    }

private:
    struct BaseImpl {
        virtual ~BaseImpl() = default;
        virtual std::any Invoke(std::vector<std::any> args) = 0;
    };

    template<typename Func>
    struct Impl : BaseImpl {
        Func func_;
        Impl(Func&& func) : func_(std::forward<Func>(func)) {}

        std::any Invoke(std::vector<std::any> args) override {
            // 利用模板推导获取函数的参数类型
            return detail::InvokeFunc(func_, std::move(args));
        }
    };

    std::unique_ptr<BaseImpl> impl_;
};
  1. 使用方式(直接传入成员函数指针):
int main() {
    Pipe pipe;
    std::map<std::string, AnyCallable> method_map;

    // 直接绑定成员函数指针和Pipe实例
    method_map["Add"] = AnyCallable(&Pipe::Add, &pipe);
    method_map["Greet"] = AnyCallable(&Pipe::Greet, &pipe);
    method_map["Print"] = AnyCallable(&Pipe::Print, &pipe);

    // 调用示例
    auto add_result = method_map["Add"]({1, 2});
    std::cout << std::any_cast<int>(add_result) << std::endl;

    auto greet_result = method_map["Greet"]({"Alice"});
    std::cout << std::any_cast<std::string>(greet_result) << std::endl;

    method_map["Print"]({"Hello RPC!"});
    return 0;
}
方案二:创建PipeMethodWrapper类包装成员函数

这种方案不需要修改原有AnyCallable,而是新增一个专门针对Pipe成员函数的包装类,通过类型擦除封装成员函数指针和Pipe实例,提供统一的调用接口。

步骤与代码实现

  1. 定义PipeMethodWrapper类:
namespace detail {
    // 辅助函数:解析参数并调用Pipe成员函数
    template<typename Ret, typename... Args, size_t... Idx>
    std::any InvokeMemberHelper(Ret (Pipe::*mem_func)(Args...), Pipe* instance, std::index_sequence<Idx...>, std::tuple<Args...>&& args) {
        if constexpr (std::is_void_v<Ret>) {
            (instance->*mem_func)(std::get<Idx>(std::move(args))...);
            return std::any();
        } else {
            return std::any((instance->*mem_func)(std::get<Idx>(std::move(args))...));
        }
    }

    template<typename Ret, typename... Args>
    std::any InvokeMemberFunc(Ret (Pipe::*mem_func)(Args...), Pipe* instance, std::tuple<Args...>&& args) {
        return InvokeMemberHelper(mem_func, instance, std::index_sequence_for<Args...>{}, std::move(args));
    }
}

class PipeMethodWrapper {
public:
    // 构造函数:接受Pipe成员函数指针和实例
    template<typename Ret, typename... Args>
    PipeMethodWrapper(Ret (Pipe::*mem_func)(Args...), Pipe* instance)
        : impl_(std::make_unique<Impl<Ret, Args...>>(mem_func, instance)) {}

    // 统一调用接口
    std::any operator()(std::vector<std::any> args) {
        return impl_->Invoke(std::move(args));
    }

private:
    struct BaseImpl {
        virtual ~BaseImpl() = default;
        virtual std::any Invoke(std::vector<std::any> args) = 0;
    };

    // 针对不同成员函数签名的具体实现
    template<typename Ret, typename... Args>
    struct Impl : BaseImpl {
        Ret (Pipe::*mem_func_)(Args...);
        Pipe* instance_;

        Impl(Ret (Pipe::*mem_func)(Args...), Pipe* instance)
            : mem_func_(mem_func), instance_(instance) {}

        std::any Invoke(std::vector<std::any> args) override {
            if (args.size() != sizeof...(Args)) {
                throw std::invalid_argument("Argument count mismatch");
            }
            // 转换参数为对应类型的tuple
            std::tuple<std::decay_t<Args>...> args_tuple;
            std::apply([&](auto&... elem) {
                size_t idx = 0;
                ((elem = std::any_cast<std::decay_t<Args>>(args[idx++])), ...);
            }, args_tuple);
            return detail::InvokeMemberFunc(mem_func_, instance_, std::move(args_tuple));
        }
    };

    std::unique_ptr<BaseImpl> impl_;
};
  1. 使用方式:
int main() {
    Pipe pipe;
    std::map<std::string, PipeMethodWrapper> method_map;

    // 直接用成员函数指针构造包装类
    method_map["Add"] = PipeMethodWrapper(&Pipe::Add, &pipe);
    method_map["Greet"] = PipeMethodWrapper(&Pipe::Greet, &pipe);
    method_map["Print"] = PipeMethodWrapper(&Pipe::Print, &pipe);

    // 调用示例
    auto add_result = method_map["Add"]({3, 4});
    std::cout << std::any_cast<int>(add_result) << std::endl;

    auto greet_result = method_map["Greet"]({"Bob"});
    std::cout << std::any_cast<std::string>(greet_result) << std::endl;

    method_map["Print"]({"PipeMethodWrapper works!"});
    return 0;
}
两种方案对比
  • 方案一:复用原有AnyCallable的类型擦除逻辑,代码更简洁,但会让AnyCallable的职责范围扩大,从包装任意可调用对象变为同时处理成员函数绑定。
  • 方案二:职责单一,专门处理Pipe成员函数的包装,对原有代码无侵入,但需要维护额外的类结构。

内容的提问来源于stack exchange,提问作者Yakumo Fujii

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 03:45:00