Skip to content

Conversation

@mikomikotaishi
Copy link

This PR adds support for C++ modules, enabled using REFLECTCPP_BUILD_MODULES. The main library is part of rfl, while each serialisation format is a partition of the module (rfl:*).

@gemini-code-assist
Copy link

Summary of Changes

Hello @mikomikotaishi, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the reflect-cpp library by introducing full support for C++20 modules. This change allows the library and its various serialization backends to be consumed as modern C++ modules, which can lead to improved compilation times and better encapsulation for projects that adopt C++20's modularity features. The integration is managed through a new CMake option, providing users with the flexibility to enable or disable module builds as needed.

Highlights

  • C++ Module Support: Introduced a new CMake option, REFLECTCPP_BUILD_MODULES, allowing users to enable the building of reflect-cpp as a C++ module. This option is set to OFF by default.
  • CMake Integration: Modified the main CMakeLists.txt to conditionally include a new src/modules subdirectory when REFLECTCPP_BUILD_MODULES is enabled. This new subdirectory contains the build logic for the C++ modules.
  • Module Definition Files: Added numerous C++ module interface units (.cppm files) under src/modules/. These files define the rfl module and its partitions for each serialization format (e.g., rfl:avro, rfl:json), exporting the necessary types and functions.
  • Documentation Update: Updated the README.md to include a new section on 'Module support', providing instructions on how to enable modules via CMake and a practical C++ code example demonstrating their usage.
  • Minor Code Adjustments: Made a minor change in include/rfl/capnproto/schema/CapnProtoTypes.hpp to declare MAP_DEFINITION as inline, and a minor formatting adjustment in include/rfl/parsing/call_destructors_on_array_where_necessary.hpp.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request adds support for C++20 modules, which is a significant and welcome improvement for modern C++ projects. The overall approach of using a primary module interface file with partitions for different serialization formats is sound. I've identified a few issues, including some critical syntax errors in the module files that will prevent compilation, a bug in the CMake configuration, and a couple of other areas for improvement regarding code correctness and maintainability. My detailed comments are below.

Comment on lines +304 to +337
export namespace std {
using std::hash;
using std::swap;

#ifdef REFLECTCPP_USE_STD_EXPECTED
using std::bad_expected_access;
#endif
}

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Exporting declarations into the std namespace (as done with export namespace std) results in undefined behavior according to the C++ standard. While this might work on some compilers, it is not portable and should be avoided. Consumers of your module should be responsible for bringing std symbols into scope, for example by importing the std module. Please remove this block.

Copy link
Author

@mikomikotaishi mikomikotaishi Jan 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is necessary as otherwise the std::* specialisations won't be accessible.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can't really find a source for this being UB. Maybe specializations can be exported explicitly though?

Copy link
Author

@mikomikotaishi mikomikotaishi Jan 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't even actually "undefined behaviour", compiler vendors do this to export the std module. My guess is the AI assumed I was adding additional non-standard symbols into namespace std, but these are official symbols. I think this is necessary to export specialisations/overloads, anyway

@mikomikotaishi mikomikotaishi force-pushed the main branch 2 times, most recently from 3b0d675 to 9137f57 Compare January 19, 2026 15:24
@liuzicheng1987
Copy link
Contributor

@mikomikotaishi awesome PR. Thank you so
much! I will take a closer look later!

@liuzicheng1987
Copy link
Contributor

@mikomikotaishi , this looks great. There is one minor comment regarding your rfl module file.

Also, we need automated tests for this and should update the Github Actions pipeline accordingly. Do you know how to do it or should I do it?

@GregTheMadMonk
Copy link
Contributor

If I'm allowed to give some input as a user, it appears to be that the module rfl includes all serialization formats unconditionally. Does this mean the library will have to be compiled in full to be used as a module? Wouldn't it be a little better to provide separate module rfl.{format} modules for every format in addition to the main module rfl, or at least not add unused formats to target_sources? I usually enable only one or two formats that I need when using rfl...

@mikomikotaishi
Copy link
Author

@liuzicheng1987 Did you leave a comment somewhere in the review? If so, I haven't been able to find it.

@GregTheMadMonk If I am understanding correctly, the module itself is built only by including the headers but is compiled, I chose to bundle the serialisation formats into the module in full because I think this offers the simplest API for users. If you have an alternate idea, I am happy to hear it but generally want to keep rfl as an umbrella module for the full library.

@liuzicheng1987
Copy link
Contributor

I did...I said that most of your includes in the rfl modules are redundant, because they are included in rfl.hpp anyway.

@liuzicheng1987
Copy link
Contributor

@mikomikotaishi

@mikomikotaishi
Copy link
Author

Apologies, I couldn't find it. Anyway, when I was originally writing the library my IDE couldn't detect the symbols until I explicitly added those #includes, so I apologise, but I am not able to manually figure out which headers are unnecessary.

@GregTheMadMonk
Copy link
Contributor

@mikomikotaishi the problem is that with the current approach the users still have to install header dependencies for all formats instead just the ones they use. This is quite tedious, and some libraries aren't very pleasant to manage as dependencies. As a user, I'd expect that module rfl at the very least doesn't reference any code unrelated to the formats that I have selected to use specifically.

IMO a good compromise would be to have module rfl.core for core library, module rfl.{format} for each format separately and then module rfl which will export import rfl.core and all format modules available for current configuration

@mikomikotaishi
Copy link
Author

mikomikotaishi commented Jan 20, 2026

That works for me, I was thinking of that.

Also, @liuzicheng1987, any thoughts on prefixing the module name with the organisation (i.e. Java-style), and making the module name something like getml.rfl or com.getml.rfl? (I originally considered this but later decided against it as I think it's important for module names to align with namespaces, but if it's a concern that rfl is a common namespace then appending the organisation name is a solution.)

@liuzicheng1987
Copy link
Contributor

@mikomikotaishi we shouldn't use Java style. The names of the modules should align with namespaces.

@liuzicheng1987
Copy link
Contributor

@mikomikotaishi , and by the way I agree with @GregTheMadMonk 's suggestion: There should be a way to only compile what you actually want. Some dependencies can be a bit tedious and forcing people to go through this even though it's completely unnecessary for what they actually want is a problem.

So I like the solution you came up with.

@mikomikotaishi
Copy link
Author

@liuzicheng1987 This would require fine-tuned configuration, because CMake has to know ahead of time which modules need to be built. Are there already such things existing in the library?

@liuzicheng1987
Copy link
Contributor

@mikomikotaishi , yes, there are flags for that in the CMake:

option(REFLECTCPP_ALL_FORMATS "Enable all supported formats" OFF)
option(REFLECTCPP_JSON "Enable JSON support" ON) # enabled by default
option(REFLECTCPP_AVRO "Enable AVRO support" ${REFLECTCPP_ALL_FORMATS})
option(REFLECTCPP_BSON "Enable BSON support" ${REFLECTCPP_ALL_FORMATS})
option(REFLECTCPP_CAPNPROTO "Enable Cap’n Proto support" ${REFLECTCPP_ALL_FORMATS})
option(REFLECTCPP_CBOR "Enable CBOR support" ${REFLECTCPP_ALL_FORMATS})
option(REFLECTCPP_CSV "Enable CSV support" ${REFLECTCPP_ALL_FORMATS})
option(REFLECTCPP_FLEXBUFFERS "Enable flexbuffers support" ${REFLECTCPP_ALL_FORMATS})
option(REFLECTCPP_MSGPACK "Enable msgpack support" ${REFLECTCPP_ALL_FORMATS})
option(REFLECTCPP_PARQUET "Enable parquet support" ${REFLECTCPP_ALL_FORMATS})
option(REFLECTCPP_XML "Enable XML support" ${REFLECTCPP_ALL_FORMATS})
option(REFLECTCPP_TOML "Enable TOML support" ${REFLECTCPP_ALL_FORMATS})
option(REFLECTCPP_UBJSON "Enable UBJSON support" ${REFLECTCPP_ALL_FORMATS})
option(REFLECTCPP_YAML "Enable YAML support" ${REFLECTCPP_ALL_FORMATS})

@mikomikotaishi
Copy link
Author

Thanks, this is perfect.

@mikomikotaishi
Copy link
Author

I've decided that I think the best way is just to keep one module, rfl, but the export import for partitions will be controlled by the build system (put these behind #ifdef). I think this is what POCO libraries does as well. If we're already controlling what gets compiled, there's no point breaking it into multiple modules if just importing one module will get the job done.

@liuzicheng1987
Copy link
Contributor

@mikomikotaishi, sure that works as well.

@mikomikotaishi
Copy link
Author

@liuzicheng1987 I've applied these changes now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants