Skip to content

Comments

mpgen: support primitive std::optional struct fields#243

Open
ryanofsky wants to merge 3 commits intobitcoin-core:masterfrom
ryanofsky:pr/optint
Open

mpgen: support primitive std::optional struct fields#243
ryanofsky wants to merge 3 commits intobitcoin-core:masterfrom
ryanofsky:pr/optint

Conversation

@ryanofsky
Copy link
Collaborator

@ryanofsky ryanofsky commented Feb 20, 2026

Currently C++ structs with primitive std::optional members (ints, bools, floats) cannot easily by mapped to Cap'n Proto structs because Cap'n Proto does not provide a way to leave primitive fields unset, so there isn't a natural way to represent std::nullopt values. This PR makes it possible to map C++ structs with fields like:

    std::optional<int> foo;

to Cap'n Proto structs by using extra Bool fields prefixed with "has" for primitive optional members:

    foo @3 :Int32;
    hasFoo @4 :Bool;

Boolean "has" fields were already supported by the code generator and used to pass primitive std::optional parameters and return values, so this PR just extends it work with all struct fields, not just fields in params and result structs.

Note: Motivation for this change is dealing with the CreatedTransactionResult::change_pos field introduced to the wallet interface in bitcoin-core/gui#807. This also could have been useful in bitcoin/bitcoin#33965 (comment)

This is a move-only change that should be easy to review with --color-moved. No
behavior is changing.
@DrahtBot
Copy link

DrahtBot commented Feb 20, 2026

The following sections might be updated with supplementary metadata relevant to reviewers and maintainers.

Reviews

See the guideline for information on the review process.
A summary of reviews will appear here.

Conflicts

Reviewers, this pull request conflicts with the following ones:

  • #242 (ipc: Serialize null CTransactionRef as empty Data by Sjors)

If you consider this pull request important, please also help to review the conflicting pull requests. Ideally, start with the one that should be merged first.

LLM Linter (✨ experimental)

Possible places where named args for integral literals may be used (e.g. func(x, /*named_arg=*/0) in C++, and func(x, named_arg=0) in Python):

  • fields.addField(schema_field, true, true) in src/mp/gen.cpp
  • fields.addField(schema_field, true, false) in src/mp/gen.cpp
  • fields.addField(schema_field, false, true) in src/mp/gen.cpp

2026-02-25 11:01:07

This doesn't change generated code at all, just noves functionality out of
Generate method into a helper method so it can be reused in the next commit.
Currently optional primitive fields like `std::optional<int>` are not well
supported as struct members.

Non-primitive optional fields like `std::optional<std::string>` and optional
struct fields are well-supported because Cap'n Proto allows non-primitive
fields to be unset, but primitive fields are always considered set so there is
natural way to represent null values.

Libmultiprocess does already support primitive optional method parameters and
result values, by allowing the .capnp files to declare extra boolean parameters
prefixed with "has" and treating the extra boolean parameters as indicators of
whether options are set or unset. This commit just this functionality to work
for struct members as well.

For example a C++ `std::optional<int> param` parameter can be represented by
'param :Int32, hasParam :Bool` parameters in a .capnp file and libmultiprocess
will use both Cap'n Proto fields together to represent the C++ value. Now C++
struct fields can be represented the same way (see unit changes test for an
example).
@Sjors
Copy link
Member

Sjors commented Feb 24, 2026

It would be useful to demonstrate that other implementations can straightforwardly handle this, e.g. with a functional tests in a Bitcoin Core branch (or PR).

@ryanofsky
Copy link
Collaborator Author

ryanofsky commented Feb 25, 2026

It would be useful to demonstrate that other implementations can straightforwardly handle this, e.g. with a functional tests in a Bitcoin Core branch (or PR).

My next rebase of bitcoin/bitcoin#10102 will use this for the change_pos field as mentioned in the PR description, but that's probably a less obvious application than a mining interface change accepting optional BlockCreateOptions. Technically bitcoin/bitcoin#33966 could do that but I think because of complexity of that PR, it's best if that PR only updates the C++ mining interface and leaves extending the Cap'n Proto interface as a followup.

In the meantime I updated the PR description with a more specific example of how this can be used and I think the unit test should provide a good test of functionality.

Updated 9f99c7d -> 4de4429 (pr/optint.1 -> pr/optint.2, compare) adding a refactoring commit deduplicating more code between parameter & struct field mapping implementations and reducing size of the last commit

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