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

如何通过cppyy将Python dict传入接收nlohmann::json的C++函数?

问题:cppyy中Python Dict转nlohmann::json的实现

问题复现

C++代码(test-json.h)

#include <iostream>
#include <nlohmann/json.hpp>

using nlohmann::json;

void print_name_and_age(json j) {
    std::cout << j["name"] << "\n"
              << j["age"] << "\n";
} 

Python代码(test-cppyy.py)

import cppyy
cppyy.include('test-json.h')

from cppyy.gbl import print_name_and_age

some_dict = {
    "name": "alfred",
    "age": 25
}

print_name_and_age(some_dict)

报错信息

print_name_and_age(some_dict)
NotImplementedError: void ::print_name_and_age(nlohmann::json j) =>
    NotImplementedError: could not convert argument 1 (this method cannot (yet) be called)

核心需求:实现Python dict直接传入接收nlohmann::json的C++函数,无需预处理编码,支持异构值(字符串、整数、浮点数、布尔值等)。


解决方案:自定义类型转换器

cppyy支持通过自定义转换器实现Python类型到C++类型的自动转换,针对nlohmann::json可按以下步骤实现:

步骤1:添加C++辅助转换函数

修改test-json.h,添加递归转换Python对象到nlohmann::json的逻辑(利用cppyy的CPyObject接口访问Python对象):

#include <iostream>
#include <nlohmann/json.hpp>
#include <cppyy/CPyCppyy.h>

using nlohmann::json;

// 递归转换Python对象到nlohmann::json
json pyobj_to_json(Cppyy::CPyObject pyobj) {
    if (PyDict_Check(pyobj)) {
        json j;
        PyObject* keys = PyDict_Keys(pyobj);
        Py_ssize_t n = PyList_Size(keys);
        for (Py_ssize_t i = 0; i < n; ++i) {
            PyObject* key = PyList_GetItem(keys, i);
            PyObject* value = PyDict_GetItem(pyobj, key);
            std::string key_str = Cppyy::CPyObject(key).As<std::string>();
            j[key_str] = pyobj_to_json(Cppyy::CPyObject(value));
        }
        Py_DECREF(keys);
        return j;
    } else if (PyList_Check(pyobj) || PyTuple_Check(pyobj)) {
        json j;
        Py_ssize_t n = PySequence_Size(pyobj);
        for (Py_ssize_t i = 0; i < n; ++i) {
            PyObject* item = PySequence_GetItem(pyobj, i);
            j.push_back(pyobj_to_json(Cppyy::CPyObject(item)));
            Py_DECREF(item);
        }
        return j;
    } else if (PyLong_Check(pyobj)) {
        return pyobj.As<long long>();
    } else if (PyFloat_Check(pyobj)) {
        return pyobj.As<double>();
    } else if (PyUnicode_Check(pyobj)) {
        return pyobj.As<std::string>();
    } else if (PyBool_Check(pyobj)) {
        return pyobj.As<bool>();
    }
    return json();
}

void print_name_and_age(json j) {
    std::cout << j["name"] << "\n"
              << j["age"] << "\n";
}

步骤2:在Python端注册转换器

修改Python代码,注册Python对象到nlohmann::json的自动转换规则:

import cppyy
cppyy.include('test-json.h')

from cppyy.gbl import print_name_and_age, pyobj_to_json
from cppyy import add_conversion

# 注册转换器:Python对象自动转nlohmann::json
add_conversion("PyObject*", "nlohmann::json", lambda pyobj: pyobj_to_json(pyobj))

some_dict = {
    "name": "alfred",
    "age": 25,
    "is_student": True,
    "scores": [90.5, 88],
    "details": {"major": "CS", "year": 3}
}

print_name_and_age(some_dict)

运行后即可正常输出,支持嵌套异构结构的转换。


替代方案:使用std::map<std::string, std::any>或std::variant

如果自定义转换器成本较高,可考虑用以下两种类型替代nlohmann::json,cppyy对其原生支持更好:

1. std::map<std::string, std::any>

cppyy可自动将Python dict转换为std::map<std::string, std::any>:

  • 要求C++17及以上版本
  • 访问值时需显式转换(如std::any_cast<int>(map["age"]))
  • 优点:无需额外代码,实现简洁
  • 缺点:类型安全性低,访问时需手动判断类型,性能略低于nlohmann::json

示例C++函数:

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

void print_name_and_age(const std::map<std::string, std::any>& config) {
    std::cout << std::any_cast<std::string>(config.at("name")) << "\n";
    std::cout << std::any_cast<int>(config.at("age")) << "\n";
}

Python端直接传入dict即可,无需额外处理。

2. std::map<std::string, std::variant<...>>

若能提前确定配置值的类型范围,std::variant比std::any更高效,cppyy同样支持自动转换:

#include <iostream>
#include <map>
#include <variant>
#include <string>

using ValueType = std::variant<std::string, int, double, bool>;

void print_name_and_age(const std::map<std::string, ValueType>& config) {
    std::visit([](const auto& val) { std::cout << val << "\n"; }, config.at("name"));
    std::visit([](const auto& val) { std::cout << val << "\n"; }, config.at("age"));
}

Python端直接传入dict,cppyy会自动匹配variant中的类型。

方案对比

方案代码简洁性性能异构支持类型安全性
自定义nlohmann::json转换器中等(需编写转换逻辑)高完全支持高(nlohmann::json自动处理类型)
std::map+std::any高(无需额外代码)中等完全支持低(需手动判断类型)
std::map+std::variant较高(需预定义类型集合)高有限支持(需提前确定类型)高(编译期类型检查)

总结

  • 优先选择自定义nlohmann::json转换器,完全匹配原有C++代码逻辑,支持任意异构结构,性能最优。
  • 若追求快速实现,std::map<std::string, std::any> 是最简洁的方案,但需注意类型安全问题。
  • 若能确定配置的类型范围,std::map<std::string, std::variant<...>> 兼顾性能与类型安全性。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.19 23:00:54