Skip to content
This repository was archived by the owner on Apr 8, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 9 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,15 @@
Support for moving `evmc::vm` objects in C++ API.
- Added: [[#357](https://github.com/ethereum/evmc/pull/357)]
The basic types `address` and `bytes32` have received their C++ wrappers
to assure they are always initialized. They also have convenient operator
overloadings for comparison and usage as keys in standard containers.
to assure they are always initialized.
They also have convenient overloaded operators for comparison
and usage as keys in standard containers.
- Added: [[#359](https://github.com/ethereum/evmc/pull/359)]
The C++ EVMC basic types `address` and `bytes32` have user defined literals.
```cpp
auto a = 0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359_address;
auto b = 0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3_bytes32;
```
- Changed: [[#293](https://github.com/ethereum/evmc/pull/293)]
In C++ API `evmc::result::raw()` renamed to `evmc::result::release_raw()`.
- Changed: [[#311](https://github.com/ethereum/evmc/pull/311)]
Expand Down
11 changes: 6 additions & 5 deletions examples/example_host.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@

#include <map>

using namespace evmc::literals;

struct account
{
evmc::uint256be balance = {};
Expand Down Expand Up @@ -108,12 +110,11 @@ class ExampleHost : public evmc::Host

evmc_bytes32 get_block_hash(int64_t number) noexcept final
{
int64_t current_block_number = get_tx_context().block_number;
const int64_t current_block_number = get_tx_context().block_number;

auto example_block_hash = evmc::bytes32{};
if (number < current_block_number && number >= current_block_number - 256)
example_block_hash = evmc::bytes32{{{1, 1, 1, 1}}};
return example_block_hash;
return (number < current_block_number && number >= current_block_number - 256) ?
0xb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5fb10c8a5f_bytes32 :
0_bytes32;
}

void emit_log(const evmc_address& addr,
Expand Down
106 changes: 105 additions & 1 deletion include/evmc/evmc.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,110 @@ constexpr bytes32::operator bool() const noexcept
return !is_zero(*this);
}

namespace literals
{
namespace internal
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Should internal stuff better go into separate header?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

So far I'm ok with having single header for C++ (now it's even better when helpers.hpp is gone).

{
template <typename T, T... Ints>
struct integer_sequence
{
};

template <uint8_t... Bytes>
using byte_sequence = integer_sequence<uint8_t, Bytes...>;

template <char... Chars>
using char_sequence = integer_sequence<char, Chars...>;


template <typename, typename>
struct concatenate;

template <uint8_t... Bytes1, uint8_t... Bytes2>
struct concatenate<byte_sequence<Bytes1...>, byte_sequence<Bytes2...>>
{
using type = byte_sequence<Bytes1..., Bytes2...>;
};

template <uint8_t D>
constexpr uint8_t parse_hex_digit() noexcept
{
static_assert((D >= '0' && D <= '9') || (D >= 'a' && D <= 'f') || (D >= 'A' && D <= 'F'),
"literal must be hexadecimal integer");
return static_cast<uint8_t>(
(D >= '0' && D <= '9') ? D - '0' : (D >= 'a' && D <= 'f') ? D - 'a' + 10 : D - 'A' + 10);
}


template <typename>
struct parse_digits;

template <uint8_t Digit1, uint8_t Digit2>
struct parse_digits<byte_sequence<Digit1, Digit2>>
{
using type = byte_sequence<static_cast<uint8_t>(parse_hex_digit<Digit1>() << 4) |
parse_hex_digit<Digit2>()>;
};

template <uint8_t Digit1, uint8_t Digit2, uint8_t... Rest>
struct parse_digits<byte_sequence<Digit1, Digit2, Rest...>>
{
using type = typename concatenate<typename parse_digits<byte_sequence<Digit1, Digit2>>::type,
typename parse_digits<byte_sequence<Rest...>>::type>::type;
};


template <typename, typename>
struct parse_literal;

template <typename T, char Prefix1, char Prefix2, char... Literal>
struct parse_literal<T, char_sequence<Prefix1, Prefix2, Literal...>>
{
static_assert(Prefix1 == '0' && Prefix2 == 'x', "literal must be in hexadecimal notation");
static_assert(sizeof...(Literal) == sizeof(T) * 2, "literal must match the result type size");

template <uint8_t... Bytes>
static constexpr T create_from(byte_sequence<Bytes...>) noexcept
{
return T{{{Bytes...}}};
}

static constexpr T get() noexcept
{
return create_from(typename parse_digits<byte_sequence<Literal...>>::type{});
}
};

template <typename T, char Digit>
struct parse_literal<T, char_sequence<Digit>>
{
static_assert(Digit == '0', "only 0 is allowed as a single digit literal");
static constexpr T get() noexcept { return {}; }
};

template <typename T, char... Literal>
constexpr T parse() noexcept
{
return parse_literal<T, char_sequence<Literal...>>::get();
}
} // namespace internal

/// Literal for evmc::address.
template <char... Literal>
constexpr address operator"" _address() noexcept
{
return internal::parse<address, Literal...>();
}

/// Literal for evmc::bytes32.
template <char... Literal>
constexpr bytes32 operator"" _bytes32() noexcept
{
return internal::parse<bytes32, Literal...>();
}
} // namespace literals

using namespace literals;

/// @copydoc evmc_result
///
Expand Down Expand Up @@ -521,7 +625,7 @@ constexpr evmc_host_interface interface{
};
} // namespace internal

inline Host::Host() noexcept : evmc_context{&internal::interface} {}
inline Host::Host() noexcept : evmc_context{&evmc::internal::interface} {}

} // namespace evmc

Expand Down
37 changes: 37 additions & 0 deletions test/unittests/test_cpp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,43 @@ TEST(cpp, bytes32_comparison)
}
}

TEST(cpp, literals)
{
using namespace evmc::literals;

#if !defined(_MSC_VER) || (_MSC_VER >= 1910 /* Only for Visual Studio 2017+ */)
constexpr auto address1 = 0xa0a1a2a3a4a5a6a7a8a9d0d1d2d3d4d5d6d7d8d9_address;
constexpr auto hash1 =
0x01020304050607080910a1a2a3a4a5a6a7a8a9b0c1c2c3c4c5c6c7c8c9d0d1d2_bytes32;
constexpr auto zero_address = 0_address;
constexpr auto zero_hash = 0_bytes32;

static_assert(address1.bytes[0] == 0xa0, "");
static_assert(address1.bytes[9] == 0xa9, "");
static_assert(address1.bytes[10] == 0xd0, "");
static_assert(address1.bytes[19] == 0xd9, "");
static_assert(hash1.bytes[0] == 0x01, "");
static_assert(hash1.bytes[10] == 0xa1, "");
static_assert(hash1.bytes[31] == 0xd2, "");
static_assert(zero_address == evmc::address{}, "");
static_assert(zero_hash == evmc::bytes32{}, "");
#endif

EXPECT_EQ(0_address, evmc::address{});
EXPECT_EQ(0_bytes32, evmc::bytes32{});

auto a1 = 0xa0a1a2a3a4a5a6a7a8a9d0d1d2d3d4d5d6d7d8d9_address;
evmc::address e1{{{0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9,
0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9}}};
EXPECT_EQ(a1, e1);

auto h1 = 0x01020304050607080910a1a2a3a4a5a6a7a8a9b0c1c2c3c4c5c6c7c8c9d0d1d2_bytes32;
evmc::bytes32 f1{{{0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0xa1,
0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xb0, 0xc1, 0xc2,
0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd0, 0xd1, 0xd2}}};
EXPECT_EQ(h1, f1);
}

TEST(cpp, result)
{
static int release_called = 0;
Expand Down