Eo provides syntactic sugars for C++ developers to write Go-like code.
Eo is not recommended for peformance-critical use case. This aims at migrating existing codebase from Go to C++ with minimum changes.
Eo is under active development and its APIs are not stable.
Eo means "I go" in Latin and its pronunciation is "Ay-Oh" like what Freddie Mercury shouted at Live Aid.
Build dependencies will be installed by Conan package manager.
- C++20 (Clang 10+) (GCC support is temporarily disabled)
- Boost 1.78 (On MacOS, use brew to install boost)
- fmt
- scope-lite
For more examples, refer here.
Goroutine is emulated by C++20 stackless coroutine and awaitable
of Boost.Asio.
func<R = void>
is an alias type of boost::asio::awaitable<R>
.
To generate awaitable function, co_await
or co_return
keyword should be in function body.
// Go
func f(s string) {
fmt.Println(s)
}
func main() {
go f("hello")
go func() {
fmt.Println("world")
}()
}
// C++
func<> f(std::string s) {
fmt::println(s);
co_return; // if co_await keyword is used in function body, this can be omitted
}
func<> eo_main() {
go(f("hello"));
go([]() -> func<> {
fmt::println("world");
co_return;
});
co_return;
}
Channel is emulated by concurrent_channel
of Boost.Asio.
Receive operator <-ch
and send operator ch <-
are replaced by *
and <<
operators.
// Go
func main() {
ch := make(chan string)
go func() { ch <- "ping" }()
msg := <-ch
fmt.Println(msg);
}
// C++
func<> eo_main() {
auto ch = make_chan<std::string>();
go([&]() -> func<> { co_await (ch << "ping"); });
auto msg = co_await *ch;
fmt::println(msg);
}
Select statement is emulated by awaitable_operators
of Boost.Asio.
This has most different syntax from that of Go.
// Go
func f() {
for {
select {
case msg := <-ch:
fmt.Println(msg)
default:
return
}
}
}
// C++
func<> f() {
auto select = Select{*ch, CaseDefault()};
for (;;) {
switch (co_await select.index()) {
case 0:
auto msg = co_await select.process<0>();
fmt::println(msg);
break;
default:
co_return;
}
}
}
Defer is emulated by scope-lite that implements experimental std::scope_exit
.
For convenience, temporary variable for assigning scope_exit
instance is generated by eo_defer
macro.
// Go
func f() {
defer fmt.Println("world")
fmt.Println("hello")
}
// C++
func<> f() {
eo_defer([]() { fmt::println("world"); });
fmt::println("hello");
}
Frequently used Go APIs are emulated, but their behaviors are not completely same.
For example, fmt
of Go is emulated by fmt
of C++, but its behavior follows original C++ API, not that of Go.