26)
```cpp
constexpr auto parse() {
// Allowed
static_assert(false, "Expected ';'");
// Not allowed :( (until C++ 26)
std::size_t line = 5;
static_assert(false, "Expected ';' at line " + to_string(line));
}
```
I didn't want my project to require C++ 26, so I used another trick. The formatted string gets turned into a static array just like a vector (into `const_string` actually) and then it's passed into `ErrorMessage<const_string Msg>` that triggers compilation error. So we force the compiler to print the full type name that includes our error. But sadly the type name has a limit about 100 symbols. I think I could solve it with splitting the message into several ErrorMessages... God, I don't want to read this in my console.
```c++
template<const_string Msg>
struct ErrorMessage {
static_assert(false, "Check the template parameter for details");
};
template<auto err_getter>
consteval auto report_error() -> void {
// C++ 26 support
#ifdef KORKA_FEATURE_FORMATTED_STATIC_ASSERT
static_assert(false, to_string(err_getter()));
#else
constexpr auto msg = const_string_from_string_view<[] { return to_string(err_getter()); }>();
std::ignore = ErrorMessage<msg>{};
#endif
}
```
I don't want you to see it, so I'll just show C++ 26 version.
```
error: static assertion failed: Lexer Error: Unterminated string at line 12
```
### Mapping signatures to names. And vice versa
In our little runtime C++ we are used to `std::unordered_map<string, value_t>` and other standard or non-standard (hello, Boost!) containers. But I needed a table where a key is a string and the value is a TYPE. And in C++ I can't treat types as values, I can't just put them into a dict... :(
So, welcome another hack!
```c++
template<auto, class>
struct signature_mapper;
// function_info_getter takes an index to our mapped function,
// and Is... holds all indices
template<auto function_info_getter, std::size_t... Is>
struct signature_mapper<
function_info_getter,
std::index_sequence<Is...>
> {
// hash func
consteval static auto hash(auto &&v) -> std::size_t {
return frozen::elsa<std::string_view>{}(v, 0);
}
// Our function overloaded with many unique types based on hash of the mapped function
constexpr static auto _overloaded = overloaded{
(
[](unique_type<hash(function_info_getter(Is).name)>)
-> const_function_info_to_signature_t<[] { return function_info_getter(Is); }> * {
return nullptr;
}
)...
};
// Extracting the type by name
template<const_string name>
using get_signature_t = std::remove_pointer_t<decltype(
_overloaded(
unique_type<hash(name)>{}
)
)>;
};
```
We use well-known function overload (~~but for evil things~~). Basically, one type inherits a lot of lambdas that take an empty `unique_type<hash>` that serves as our key and returns the pointer to our type.
```cpp
// How our mapper looks after expanding our params
struct overloaded : lambda1, lambda2, lambda3 {
using lambda1::operator();
using lambda2::operator();
using lambda3::operator();
};
// And every lambda looks like this
auto lambda_fib = [](unique_type<hash("fib")>) -> signature_of_fib* { return nullptr; };
```
When we call `_overloaded(unique_type<hash(name)>())` our poor compiler has to resolve the overload. And he looks for right one through all `()` operators. And then we just take that it returns (our `T*`) and get the `T`.
I use this "mechanism" to extract script functions into the native C++.
```cpp
constexpr auto script_fib = compile_result.function<"fib">();
```
### Bindings from C++ to our script lang
This was the most exhausting part. Well, how "exhausting" exactly... I was thinking for a few evenings and then made it work one morning. The problem was with me. I wanted to make a pretty API that was impossible in the current standard (maybe it's possible in C++ 26, but I didn't check it).
I wanted it to look like this:
```cpp
auto func() -> void;
auto foo(int) -> int;
// Примерно так
constexpr auto bindings = korka::make_bindings<
"func", func,
"foo", foo
>();
// Или так
constexpr auto
Post #25689
10