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

C++中ID与通用回调映射的可行性咨询及实现方案

Feasibility and Fixes for ID-to-Generic-Callback Mapping

Your core idea of mapping IDs to callbacks with different signatures is absolutely feasible—we just need to fix the type homogeneity issue in the map and correct the template usage. Let's break down the problems in your code and walk through two solid solutions.

Why Your Current Code Fails

There are two critical issues:

  1. Missing Template Arguments: Callback is a template class, so std::map<int, Callback> is invalid—you must specify the template parameters (e.g., Callback<bool, int>).
  2. Type Mismatch in the Map: A std::map requires all values to be the same type. Since Class1::run takes an int and Class2::run takes a std::string, their corresponding Callback instantiations (Callback<bool, int> and Callback<bool, std::string>) are distinct types that can't coexist directly in the same map.

We solve this with type erasure—a way to store objects of different types in a homogeneous container.


Solution 1: Using std::any (Simplest Approach)

std::any (C++17+) lets you store any type in a single container. We'll use it to hold our different Callback instances, then cast back to the correct type when invoking the callback.

Corrected Code

#include <iostream>
#include <map>
#include <functional>
#include <any>
#include <string>

class Class1 {
public:
    bool run(const int &temp){ 
        std::cout << "worker1:" << temp << std::endl; 
        return false; // Using false instead of 0 for clarity
    }
};

class Class2 {
public:
    bool run(const std::string &temp){ 
        std::cout << "worker2:" << temp << std::endl; 
        return false;
    }
};

template <typename ReturnType, typename... Args>
class Callback {
private:
    std::function<ReturnType(Args...)> _function;
public:
    Callback(std::function<ReturnType(Args...)> function) : _function(std::move(function)) {}
    
    ReturnType run(Args... args) {
        return _function(std::forward<Args>(args)...);
    }
};

int main() {
    Class1 class1;
    Class2 class2;
    
    std::map<int, std::any> cbs;
    
    // Store callbacks with their respective types
    cbs[1] = Callback<bool, int>(std::bind(&Class1::run, &class1, std::placeholders::_1));
    // Alternatively, use a lambda for readability:
    // cbs[2] = Callback<bool, std::string>([&class2](const std::string& temp) { return class2.run(temp); });
    cbs[2] = Callback<bool, std::string>(std::bind(&Class2::run, &class2, std::placeholders::_1));
    
    // Safely retrieve and invoke callbacks
    if (auto* cb = std::any_cast<Callback<bool, int>>(&cbs[1])) {
        cb->run(1);
    }
    
    if (auto* cb = std::any_cast<Callback<bool, std::string>>(&cbs[2])) {
        cb->run("string msg");
    }
    
    return 0;
}

Key Notes:

  • We use std::any_cast with a pointer to avoid exceptions if the type doesn't match (returns nullptr instead).
  • Lambdas are often more readable than std::bind—I included both options for flexibility.

Solution 2: Polymorphic Base Class (More Flexible)

If you need more control over callback behavior (e.g., adding common methods to all callbacks), use a polymorphic base class with dynamic casting.

Corrected Code

#include <iostream>
#include <map>
#include <functional>
#include <memory>
#include <string>

class CallbackBase {
public:
    virtual ~CallbackBase() = default; // Required for proper cleanup of derived objects
};

template <typename ReturnType, typename... Args>
class Callback : public CallbackBase {
private:
    std::function<ReturnType(Args...)> _function;
public:
    Callback(std::function<ReturnType(Args...)> function) : _function(std::move(function)) {}
    
    ReturnType run(Args... args) {
        return _function(std::forward<Args>(args)...);
    }
};

class Class1 {
public:
    bool run(const int &temp){ 
        std::cout << "worker1:" << temp << std::endl; 
        return false;
    }
};

class Class2 {
public:
    bool run(const std::string &temp){ 
        std::cout << "worker2:" << temp << std::endl; 
        return false;
    }
};

int main() {
    Class1 class1;
    Class2 class2;
    
    std::map<int, std::unique_ptr<CallbackBase>> cbs;
    
    // Store callbacks as unique_ptr to the base class
    cbs[1] = std::make_unique<Callback<bool, int>>(std::bind(&Class1::run, &class1, std::placeholders::_1));
    cbs[2] = std::make_unique<Callback<bool, std::string>>([&class2](const std::string& temp) { return class2.run(temp); });
    
    // Dynamic cast to the correct derived type and invoke
    if (auto* cb = dynamic_cast<Callback<bool, int>*>(cbs[1].get())) {
        cb->run(1);
    }
    
    if (auto* cb = dynamic_cast<Callback<bool, std::string>*>(cbs[2].get())) {
        cb->run("string msg");
    }
    
    return 0;
}

Key Notes:

  • CallbackBase provides a common interface for all callbacks, and std::unique_ptr manages memory safely to avoid leaks.
  • dynamic_cast checks if the stored object matches the expected type before invoking, preventing undefined behavior.

Both solutions work well—choose the one that fits your needs. The std::any approach is simpler for most cases, while the polymorphic approach is better if you plan to extend callback functionality later.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.07 11:02:28