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

C++17下用现代化通用可变参数宏简化JSON反序列化的方案问询

Awesome question—when you're dealing with hundreds of ECS components, repetitive from_json boilerplate is the last thing you need. Since you're using C++17, we can leverage variadic templates and modern language features to build a clean, scalable solution that’s almost as concise as the macro syntax you’re imagining. Let’s break down the best approaches:


Pure C++17 Solution (No Third-Party Libraries)

This approach uses custom templates and a minimal macro to eliminate repetitive code, sticking strictly to C++17 features.

Step 1: Build Variadic Deserialization Helpers

First, create helper functions to handle single and multiple member deserialization using C++17 fold expressions:

namespace JSON {
    // Deserialize a single struct member
    template <typename T, typename MemberType>
    void deserialize_single(T& obj, const Node& json, MemberType T::*member_ptr, const char* key_name) {
        json_get(obj.*member_ptr, key_name, json);
    }

    // Expand deserialization for all members via fold expression
    template <typename T, typename... Args>
    void deserialize_members(T& obj, const Node& json, Args&&... args) {
        (deserialize_single(obj, json, std::forward<Args>(args)), ...);
    }
}

Step 2: Create the Concise Registration Macro

Wrap the helpers in a macro that matches the syntax you want. This macro takes your struct type and member names, then generates the full from_json function automatically:

// Helper to pair a member pointer with its stringified name
#define _JSON_MEMBER_PAIR(Type, Member) &Type::Member, #Member

// Boost.Preprocessor (optional but cleaner for arbitrary member counts)
#include <boost/preprocessor/seq/for_each_i.hpp>
#include <boost/preprocessor/variadic/to_seq.hpp>

#define _JSON_EXPAND_PAIR(r, Type, i, Member) , _JSON_MEMBER_PAIR(Type, Member)

// Main macro: Use like JSON(MyStruct, propertyA, propertyB, propertyC)
#define JSON(Type, ...) \
namespace JSON { \
    void from_json(const Node& json, Type& obj) { \
        deserialize_members(obj, json, \
            _JSON_MEMBER_PAIR(Type, BOOST_PP_SEQ_HEAD(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))) \
            BOOST_PP_SEQ_FOR_EACH_I(_JSON_EXPAND_PAIR, Type, BOOST_PP_SEQ_TAIL(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))) \
        ); \
    } \
}

// If you can't use Boost, here's a simpler (limited) version for up to 5 members:
// #define JSON(Type, M1, M2, M3...) \
// namespace JSON { \
//     void from_json(const Node& json, Type& obj) { \
//         deserialize_members(obj, json, \
//             _JSON_MEMBER_PAIR(Type, M1), _JSON_MEMBER_PAIR(Type, M2), _JSON_MEMBER_PAIR(Type, M3) \
//         ); \
//     } \
// }

Usage Example

Register your component in one line:

struct MyStruct { int propertyA; std::string propertyB; std::vector<int> propertyC; };

// Generates the full from_json function automatically
JSON(MyStruct, propertyA, propertyB, propertyC);

Even Cleaner: Boost.PFR (Compile-Time Reflection)

If you’re open to a lightweight Boost dependency, Boost.PFR (Plain Old Data Reflection) lets you eliminate macros entirely. It works for any aggregate type (public members, no custom constructors)—perfect for ECS components.

Setup

Include Boost.PFR and write a generic from_json template:

#include <boost/pfr.hpp>

namespace JSON {
    // Generic from_json for all flat aggregate types
    template <typename T>
    std::enable_if_t<boost::pfr::is_flat_v<T>>
    from_json(const Node& json, T& obj) {
        // Iterate over all struct fields at compile time
        boost::pfr::for_each_field(obj, [&](auto& member, std::size_t idx) {
            // Get the member's name as a compile-time string
            const char* member_name = boost::pfr::get_name<idx, T>();
            json_get(member, member_name, json);
        });
    }
}

Usage Example

No extra code needed—just define your struct:

struct MyStruct { int propertyA; std::string propertyB; std::vector<int> propertyC; };

// from_json is automatically generated for you!

Key Takeaways

  • C++17 Features Used: Fold expressions, variadic templates, compile-time stringification, and (with Boost.PFR) compile-time reflection.
  • ECS Scalability: Both solutions scale seamlessly for hundreds of components—no more copying and pasting json_get lines.
  • Flexibility: The pure C++17 solution lets you override JSON keys (replace #Member with a custom string), while Boost.PFR uses member names directly for zero-config deserialization.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 15:27:49