Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Modularizing (#1586) #1599

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft

Modularizing (#1586) #1599

wants to merge 1 commit into from

Conversation

msqr1
Copy link

@msqr1 msqr1 commented Feb 7, 2025

More to be added, this is just a draft one. Modularized with importizer, code is not reviewed, fresh after running it through.
Regular build works perfectly, you can use the same command to build and test with zero changes. Working on exporting and writing the CMake portion for modules.

@msqr1
Copy link
Author

msqr1 commented Feb 7, 2025

@stephenberry Do you have any thoughts on the code before I move on to actually supporting modules? Also do you want to try the new module structure or the current one is OK?

@msqr1 msqr1 marked this pull request as draft February 7, 2025 03:32
@stephenberry
Copy link
Owner

Can you explain a little bit more the new approach? Does this have to do with internal macros to the library? Glaze does have some macros that are used across the library.

Note that I noticed some weird handling of macros that are locally defined. See the modularization of glaze/api/trait.hpp.

module;
#include <array>
#define std_trait(x) static constexpr sv x = to_sv<std::x##_v<T>>()
#undef std_trait
export module glaze.api.trait;

This std_trait macro is defined and undefined within the same header. But, your module code adds it to the top in a manner that does nothing.

@msqr1
Copy link
Author

msqr1 commented Feb 7, 2025

The current approach is that we have a section for module specific stuff and a section for header specific stuff. The new approach is that we have 3 sections: shared, module, and header. This allows for less repeating stuff. For example, you can include a 3rd party header or Export.hpp only once, especially not duplicating standard includes. (Example in the link I gave). Functionality is exactly the same as this, just that it can generate shorter preambles.

Also about the macro thing, keep in mind that this is just the output from the program, I didn't do any clean up yet. Though I try my best, my DCE pass is not so good. If you had an empty conditional macro, it will get eliminated, but I didn't think about the #define/#undef case lol.

@stephenberry
Copy link
Owner

Thanks for your explanation. It sounds like the new approach is better. You'll probably be dropping the old approach, right? In which case I think it's better to move to the new.

Yeah, I realize this is auto-generated from importizer. So, I figured I'd point out things that you can improve/fix. The #define/#undef is probably the best use of macros because it doesn't pollute anything, but can locally shorten code.

@msqr1
Copy link
Author

msqr1 commented Feb 7, 2025

I will work on those, and let's see how it goes. Right now, the biggest challenge is shortening the preamble in transitional mode.

@msqr1
Copy link
Author

msqr1 commented Feb 10, 2025

@stephenberry can you check it out? I implemented the shared code section as well as the #define/#undef removal.
Let me know if you are ready for exporting and cleaning up!

@stephenberry
Copy link
Owner

This is looking good. We should be able to add to the GitHub Actions and test building with modules.
I'm also curious about your thoughts on import std? What do you think needs to happen with compilers before it can be used?
Thanks!

@msqr1
Copy link
Author

msqr1 commented Feb 11, 2025

Std module status:
GCC: Incomplete
Clang: 19
MSVC: 14.38

If you want to use import std right now to speed up compilation, we have to drop GCC. The best way right now to do use standard library as a module is bootstrapping it. By that I mean something like this:

module;
#include <vector>
// Other standard includes
export module stdBootstrap;
using namespace std;
export namespace std {
  using vector;
  // using ... 
}

We get better compile time this way. We can swap out for compilers' standard modules once GCC is ready with some Ctrl+F. And you might have guessed it, I have a setting to do just this...

@stephenberry
Copy link
Owner

Thanks for the info. I don't think import std needs to be used right now, but it's helpful to know the state of things.

An additional thought is that you might want to look into a way of handling git renaming so that the pull request doesn't show every line of every file as having changed. Git has methods to rename files so that changing to .cppm doesn't look like the entire file changed.

@msqr1
Copy link
Author

msqr1 commented Feb 11, 2025

An additional thought is that you might want to look into a way of handling git renaming so that the pull request doesn't show every line of every file as having changed. Git has methods to rename files so that changing to .cppm doesn't look like the entire file changed.

I will get to this at the very end. Are you ready for cleaning? Any last fixes? @stephenberry

@stephenberry
Copy link
Owner

I don't see any problems, so I say keep going ahead!

@msqr1
Copy link
Author

msqr1 commented Feb 12, 2025

@stephenberry One more thing I forgot, glaze seems to have some headers depending on macros from other headers. While this would work in header build, it won't be in modules because modules don't leak macros. Local macros like GLZ_CSV_NL, DLL_EXPORT are fine. For glaze, there are a few way out: put the macros in a separate file, and include that file in those that needed the macros, put the macro definitions on the command line using -D. What do you want to do? I am seeing this happen with: GLZ_ALWAYS_INLINE, [most of the macros in util/parse.hpp] (what else?)

@msqr1
Copy link
Author

msqr1 commented Feb 12, 2025

I also replaced the static_assert(false, "Eigen must be included to use glaze/ext/eigen.hpp"); and static_assert(false, "standalone or boost asio must be included to use glaze/ext/glaze_asio.hpp"); with #error because my tool doesn't recognize a static_assert(false, ...); as a preprocessor directive, is that ok?

@stephenberry
Copy link
Owner

I also replaced the static_assert(false, "Eigen must be included to use glaze/ext/eigen.hpp"); and static_assert(false, "standalone or boost asio must be included to use glaze/ext/glaze_asio.hpp"); with #error because my tool doesn't recognize a static_assert(false, ...); as a preprocessor directive, is that ok?

Yes, this sounds fine.

@stephenberry
Copy link
Owner

@stephenberry One more thing I forgot, glaze seems to have some headers depending on macros from other headers. While this would work in header build, it won't be in modules because modules don't leak macros. Local macros like GLZ_CSV_NL, DLL_EXPORT are fine. For glaze, there are a few way out: put the macros in a separate file, and include that file in those that needed the macros, put the macro definitions on the command line using -D. What do you want to do? I am seeing this happen with: GLZ_ALWAYS_INLINE, [most of the macros in util/parse.hpp] (what else?)

Yeah, the macros in util/parse.hpp are also useful for users writing custom implementations. So, I think they'll need to exist in a header that developers can also include. I think this solution is best:

...put the macros in a separate file, and include that file in those that needed the macros...

Note that C++ doesn't have a mechanism of optionally returning from an inlined function call. So, macros are needed to keep code reasonably clean when writing code without exceptions and needing to return on error. Hence, the reason for most of these macros.

@msqr1 msqr1 changed the title Modularizing Modularizing (#1586) Feb 12, 2025
@msqr1
Copy link
Author

msqr1 commented Feb 12, 2025

Note that C++ doesn't have a mechanism of optionally returning from an inlined function call. So, macros are needed to keep code reasonably clean when writing code without exceptions and needing to return on error. Hence, the reason for most of these macros.

Can you elaborate further on this and give an example?
@stephenberry

@stephenberry
Copy link
Owner

Here's an example of a macro:

#define GLZ_INVALID_END(RETURN)                  \
   if constexpr (not Opts.null_terminated) {     \
      if (it == end) [[unlikely]] {              \
         ctx.error = error_code::unexpected_end; \
         return RETURN;                          \
      }                                          \
   }

This can be used simply by adding the line:

GLZ_INVALID_END();

To do this with a forced inline function call we could write the function as:

template <auto Opts>
GLZ_ALWAYS_INLINE void invalid_end(context& ctx, auto& it) noexcept {
   if constexpr (not Opts.null_terminated) { 
      if (it == end) [[unlikely]] {
         ctx.error = error_code::unexpected_end;
      }
   }
}

To use this function we need to write:

invalid_end<Opts>(ctx, it);
if (bool(ctx.error)) [[unlikely]] {
   return;
}

Not only is this more typing, but it actually creates two checks for the error rather than just one. So, it has double the branching. Even if it is optimized away in Release builds, it still makes Debug builds slower. And, the force inline tends to increase compilation times (especially on MSVC). So, the macro approach can build much faster.

Now, consider functions that might have multiple return points. The optimizer begins to struggle more when the error paths become more complex. If we need to add this additional check after calling our function then we can end up with more branching than the macro code, and in order to force inline the function we also hurt compilation times.

@stephenberry
Copy link
Owner

In looking at the error handling problem again I discovered a way to get as efficient assembly with the non-macro approach in Release mode. It looks like I can avoid using force inlining for at least the simpler functions. There will still be additional overhead in Debug builds, but I think it is worth it to eliminate macros. I'll do more experimentation later.

@msqr1
Copy link
Author

msqr1 commented Feb 13, 2025

I'm also curious about how you do this. Let me know so that I can update my part.

@stephenberry
Copy link
Owner

Here's an example of using a boolean return that generates just as efficient assembly for GCC and Release. The important things are that I return a boolean and not an error code, and that the context error code is only set in the error path.

#include <array>
#include <bit>
#include <concepts>
#include <cstddef>
#include <cstdint>

enum struct error_code : uint32_t
{
    none,
    expected_bracket,
    expected_brace,
    expected_quote
};

struct context
{
    error_code error;
};

inline bool match_bracket(context& ctx, char*& it) noexcept
{
    if (*it != '[') [[unlikely]] {
        ctx.error = error_code::expected_bracket;
        return true;
    }
    else [[likely]] {
        ++it;
        return false;
    }
}

void do_something(context& ctx, char*& it);

void func(context& ctx, char*& it)
{
    if (match_bracket(ctx, it)) [[unlikely]] {
        return;
    }
    do_something(ctx, it);
}

void func2(context& ctx, char*& it)
{
    if (*it != '[') [[unlikely]] {
        ctx.error = error_code::expected_bracket;
        return;
    }
    else [[likely]] {
        ++it;
    }
    do_something(ctx, it);
}

@msqr1
Copy link
Author

msqr1 commented Feb 13, 2025

That was what I had in mind, too, yesterday. Returning a bool, use it as rvalue for RVO to kick in. Can you replace the macros so that I can update them on my end? @stephenberry

@stephenberry
Copy link
Owner

Yes, I'll get to work on replacing macros.

@stephenberry
Copy link
Owner

I'm removing macros in this pull request: #1614

@msqr1
Copy link
Author

msqr1 commented Feb 15, 2025

Just making sure, @stephenberry, the only non-local macros left are in util/inline.hpp, right? Are there any other files that provides macro definitions for others?

@stephenberry
Copy link
Owner

Yes, I think the only macros that need to be shared in order to build are those in util/inline.hpp. Let me know if you discover otherwise.

@msqr1 msqr1 reopened this Feb 18, 2025
@msqr1
Copy link
Author

msqr1 commented Feb 19, 2025

I'm still trying to set up CMake modules build. It doesn't work at all, I need some help lol @stephenberry

@stephenberry
Copy link
Owner

@msqr1, I'll look into this when I have a chunk of time. I'm a bit preoccupied by other work at the moment, but I am really excited about your work and eager to help.

@msqr1
Copy link
Author

msqr1 commented Feb 25, 2025

Hi, I got these error while trying to build normally with clang(++)-19 and libc++:

glaze-modularized/tests/beve_test/beve_test.cpp:1443:12: error: explicitly defaulted three-way comparison operator is implicitly deleted [-Werror,-Wdefaulted-function-deleted]
 1443 |       auto operator<=>(const A1&) const = default;
      |            ^
glaze-modularized/tests/beve_test/beve_test.cpp:1441:35: note: defaulted 'operator<=>' is implicitly deleted because there is no viable three-way comparison function for member 'a'
 1441 |       std::map<uint8_t, uint64_t> a{};
      |                                   ^
/usr/include/c++/v1/__threading_support:680:17: note: candidate function not viable: no known conversion from 'const std::map<uint8_t, uint64_t>' (aka 'const map<unsigned char, unsigned long>') to '__thread_id' for 1st argument
  680 | strong_ordering operator<=>(__thread_id __x, __thread_id __y) noexcept {
      |                 ^           ~~~~~~~~~~~~~~~
/usr/include/c++/v1/system_error:481:1: note: candidate function not viable: no known conversion from 'const std::map<uint8_t, uint64_t>' (aka 'const map<unsigned char, unsigned long>') to 'const error_code' for 1st argument
  481 | operator<=>(const error_code& __x, const error_code& __y) noexcept
      | ^           ~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/v1/system_error:489:1: note: candidate function not viable: no known conversion from 'const std::map<uint8_t, uint64_t>' (aka 'const map<unsigned char, unsigned long>') to 'const error_condition' for 1st argument
  489 | operator<=>(const error_condition& __x, const error_condition& __y) noexcept
      | ^           ~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/v1/__variant/monostate.h:32:49: note: candidate function not viable: no known conversion from 'const std::map<uint8_t, uint64_t>' (aka 'const map<unsigned char, unsigned long>') to 'monostate' for 1st argument
   32 | _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(monostate, monostate) noexcept {
      |                                                 ^           ~~~~~~~~~
/usr/include/c++/v1/__utility/pair.h:444:1: note: candidate template ignored: could not match 'pair' against 'std::map'
  444 | operator<=>(const pair<_T1,_T2>& __x, const pair<_T1,_T2>& __y)
      | ^
/usr/include/c++/v1/__iterator/reverse_iterator.h:288:1: note: candidate template ignored: could not match 'reverse_iterator' against 'std::map'
  288 | operator<=>(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
      | ^
/usr/include/c++/v1/__iterator/reverse_iterator.h:288:1: note: candidate template ignored: could not match 'reverse_iterator' against 'std::map'
/usr/include/c++/v1/tuple:1572:1: note: candidate template ignored: could not match 'tuple' against 'std::map'
 1572 | operator<=>(const tuple<_Tp...>& __x, const tuple<_Up...>& __y)
      | ^
/usr/include/c++/v1/tuple:1572:1: note: candidate template ignored: could not match 'tuple' against 'std::map'
/usr/include/c++/v1/string_view:784:1: note: candidate template ignored: could not match 'basic_string_view' against 'std::map'
  784 | operator<=>(basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_CharT, _Traits> __rhs) noexcept {
      | ^
/usr/include/c++/v1/string_view:797:38: note: candidate template ignored: could not match 'basic_string_view' against 'std::map'
  797 | _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(
      |                                      ^
/usr/include/c++/v1/string_view:797:38: note: candidate template ignored: could not match 'basic_string_view' against 'std::map'
/usr/include/c++/v1/string:4077:38: note: candidate template ignored: could not match 'basic_string' against 'std::map'
 4077 | _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(
      |                                      ^
/usr/include/c++/v1/string:4085:1: note: candidate template ignored: could not match 'basic_string' against 'std::map'
 4085 | operator<=>(const basic_string<_CharT, _Traits, _Allocator>& __lhs, const _CharT* __rhs) {
      | ^
/usr/include/c++/v1/string:4085:1: note: candidate template ignored: could not match 'basic_string' against 'std::map'
/usr/include/c++/v1/__memory/unique_ptr.h:566:1: note: candidate template ignored: could not match 'unique_ptr' against 'std::map'
  566 | operator<=>(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {
      | ^
/usr/include/c++/v1/__memory/unique_ptr.h:566:1: note: candidate template ignored: could not match 'unique_ptr' against 'std::map'
/usr/include/c++/v1/__memory/unique_ptr.h:658:1: note: candidate template ignored: could not match 'unique_ptr' against 'std::map'
  658 | operator<=>(const unique_ptr<_T1, _D1>& __x, nullptr_t) {
      | ^
/usr/include/c++/v1/__memory/unique_ptr.h:658:1: note: candidate template ignored: could not match 'unique_ptr' against 'std::map'
/usr/include/c++/v1/__memory/shared_ptr.h:1374:1: note: candidate template ignored: could not match 'shared_ptr' against 'std::map'
 1374 | operator<=>(shared_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) noexcept
      | ^
/usr/include/c++/v1/__memory/shared_ptr.h:1374:1: note: candidate template ignored: could not match 'shared_ptr' against 'std::map'
/usr/include/c++/v1/__memory/shared_ptr.h:1483:1: note: candidate template ignored: could not match 'shared_ptr' against 'std::map'
 1483 | operator<=>(shared_ptr<_Tp> const& __x, nullptr_t) noexcept
      | ^
/usr/include/c++/v1/__memory/shared_ptr.h:1483:1: note: candidate template ignored: could not match 'shared_ptr' against 'std::map'
/usr/include/c++/v1/__iterator/move_iterator.h:282:6: note: candidate template ignored: could not match 'move_iterator' against 'std::map'
  282 | auto operator<=>(const move_iterator<_Iter1>& __x, const move_iterator<_Iter2>& __y)
      |      ^
/usr/include/c++/v1/__iterator/move_iterator.h:282:6: note: candidate template ignored: could not match 'move_iterator' against 'std::map'
/usr/include/c++/v1/variant:1659:1: note: candidate template ignored: could not match 'variant' against 'std::map'
 1659 | operator<=>(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) {
      | ^
glaze-modularized/tests/beve_test/beve_test.cpp:1443:43: note: replace 'default' with 'delete'
 1443 |       auto operator<=>(const A1&) const = default;
      |                                           ^~~~~~~
      |                                           delete
glaze-modularized/tests/beve_test/beve_test.cpp:1451:12: error: explicitly defaulted three-way comparison operator is implicitly deleted [-Werror,-Wdefaulted-function-deleted]
 1451 |       auto operator<=>(const B&) const = default;
      |            ^
glaze-modularized/tests/beve_test/beve_test.cpp:1449:10: note: defaulted 'operator<=>' is implicitly deleted because it would invoke a deleted comparison function for member 'a'
 1449 |       A1 a{};
      |          ^
glaze-modularized/tests/beve_test/beve_test.cpp:1443:12: note: explicitly defaulted function was implicitly deleted here
 1443 |       auto operator<=>(const A1&) const = default;
      |            ^
glaze-modularized/tests/beve_test/beve_test.cpp:1441:35: note: defaulted 'operator<=>' is implicitly deleted because there is no viable three-way comparison function for member 'a'
 1441 |       std::map<uint8_t, uint64_t> a{};
      |                                   ^
/usr/include/c++/v1/__threading_support:680:17: note: candidate function not viable: no known conversion from 'const std::map<uint8_t, uint64_t>' (aka 'const map<unsigned char, unsigned long>') to '__thread_id' for 1st argument
  680 | strong_ordering operator<=>(__thread_id __x, __thread_id __y) noexcept {
      |                 ^           ~~~~~~~~~~~~~~~
/usr/include/c++/v1/system_error:481:1: note: candidate function not viable: no known conversion from 'const std::map<uint8_t, uint64_t>' (aka 'const map<unsigned char, unsigned long>') to 'const error_code' for 1st argument
  481 | operator<=>(const error_code& __x, const error_code& __y) noexcept
      | ^           ~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/v1/system_error:489:1: note: candidate function not viable: no known conversion from 'const std::map<uint8_t, uint64_t>' (aka 'const map<unsigned char, unsigned long>') to 'const error_condition' for 1st argument
  489 | operator<=>(const error_condition& __x, const error_condition& __y) noexcept
      | ^           ~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/include/c++/v1/__variant/monostate.h:32:49: note: candidate function not viable: no known conversion from 'const std::map<uint8_t, uint64_t>' (aka 'const map<unsigned char, unsigned long>') to 'monostate' for 1st argument
   32 | _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(monostate, monostate) noexcept {
      |                                                 ^           ~~~~~~~~~
/usr/include/c++/v1/__utility/pair.h:444:1: note: candidate template ignored: could not match 'pair' against 'std::map'
  444 | operator<=>(const pair<_T1,_T2>& __x, const pair<_T1,_T2>& __y)
      | ^
/usr/include/c++/v1/__iterator/reverse_iterator.h:288:1: note: candidate template ignored: could not match 'reverse_iterator' against 'std::map'
  288 | operator<=>(const reverse_iterator<_Iter1>& __x, const reverse_iterator<_Iter2>& __y)
      | ^
/usr/include/c++/v1/__iterator/reverse_iterator.h:288:1: note: candidate template ignored: could not match 'reverse_iterator' against 'std::map'
/usr/include/c++/v1/tuple:1572:1: note: candidate template ignored: could not match 'tuple' against 'std::map'
 1572 | operator<=>(const tuple<_Tp...>& __x, const tuple<_Up...>& __y)
      | ^
/usr/include/c++/v1/tuple:1572:1: note: candidate template ignored: could not match 'tuple' against 'std::map'
/usr/include/c++/v1/string_view:784:1: note: candidate template ignored: could not match 'basic_string_view' against 'std::map'
  784 | operator<=>(basic_string_view<_CharT, _Traits> __lhs, basic_string_view<_CharT, _Traits> __rhs) noexcept {
      | ^
/usr/include/c++/v1/string_view:797:38: note: candidate template ignored: could not match 'basic_string_view' against 'std::map'
  797 | _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(
      |                                      ^
/usr/include/c++/v1/string_view:797:38: note: candidate template ignored: could not match 'basic_string_view' against 'std::map'
/usr/include/c++/v1/string:4077:38: note: candidate template ignored: could not match 'basic_string' against 'std::map'
 4077 | _LIBCPP_HIDE_FROM_ABI constexpr auto operator<=>(
      |                                      ^
/usr/include/c++/v1/string:4085:1: note: candidate template ignored: could not match 'basic_string' against 'std::map'
 4085 | operator<=>(const basic_string<_CharT, _Traits, _Allocator>& __lhs, const _CharT* __rhs) {
      | ^
/usr/include/c++/v1/string:4085:1: note: candidate template ignored: could not match 'basic_string' against 'std::map'
/usr/include/c++/v1/__memory/unique_ptr.h:566:1: note: candidate template ignored: could not match 'unique_ptr' against 'std::map'
  566 | operator<=>(const unique_ptr<_T1, _D1>& __x, const unique_ptr<_T2, _D2>& __y) {
      | ^
/usr/include/c++/v1/__memory/unique_ptr.h:566:1: note: candidate template ignored: could not match 'unique_ptr' against 'std::map'
/usr/include/c++/v1/__memory/unique_ptr.h:658:1: note: candidate template ignored: could not match 'unique_ptr' against 'std::map'
  658 | operator<=>(const unique_ptr<_T1, _D1>& __x, nullptr_t) {
      | ^
/usr/include/c++/v1/__memory/unique_ptr.h:658:1: note: candidate template ignored: could not match 'unique_ptr' against 'std::map'
/usr/include/c++/v1/__memory/shared_ptr.h:1374:1: note: candidate template ignored: could not match 'shared_ptr' against 'std::map'
 1374 | operator<=>(shared_ptr<_Tp> const& __x, shared_ptr<_Up> const& __y) noexcept
      | ^
/usr/include/c++/v1/__memory/shared_ptr.h:1374:1: note: candidate template ignored: could not match 'shared_ptr' against 'std::map'
/usr/include/c++/v1/__memory/shared_ptr.h:1483:1: note: candidate template ignored: could not match 'shared_ptr' against 'std::map'
 1483 | operator<=>(shared_ptr<_Tp> const& __x, nullptr_t) noexcept
      | ^
/usr/include/c++/v1/__memory/shared_ptr.h:1483:1: note: candidate template ignored: could not match 'shared_ptr' against 'std::map'
/usr/include/c++/v1/__iterator/move_iterator.h:282:6: note: candidate template ignored: could not match 'move_iterator' against 'std::map'
  282 | auto operator<=>(const move_iterator<_Iter1>& __x, const move_iterator<_Iter2>& __y)
      |      ^
/usr/include/c++/v1/__iterator/move_iterator.h:282:6: note: candidate template ignored: could not match 'move_iterator' against 'std::map'
/usr/include/c++/v1/variant:1659:1: note: candidate template ignored: could not match 'variant' against 'std::map'
 1659 | operator<=>(const variant<_Types...>& __lhs, const variant<_Types...>& __rhs) {
      | ^
glaze-modularized/tests/beve_test/beve_test.cpp:1451:42: note: replace 'default' with 'delete'
 1451 |       auto operator<=>(const B&) const = default;
      |                                          ^~~~~~~
      |                                          delete
2 errors generated.
ninja: build stopped: subcommand failed.

In tests/beve_test/beve_test.cpp, just literally relace default with delete and it will work (line 1443 and 1451). If you know why, could you tell me? @stephenberry

@stephenberry
Copy link
Owner

@msqr1, this first line tells the error:
error: explicitly defaulted three-way comparison operator is implicitly deleted [-Werror,-Wdefaulted-function-deleted]

This would typically be a warning, but warnings are considered errors with Werror. It is just letting the programmer know that the default spaceship operator is deleted and therefore should be marked delete to be more clear.

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.

2 participants