Skip to content

xyproto/go2cpp

Repository files navigation

go2cpp

Compiles Go to native executables via C++20.

One of the goals is for the compiler to be able to compile itself.

The intended use is not to convert entire existing Go programs to C++, but to help port parts of it to C++, or perhaps write programs from scratch and continually check that the program can be converted and compiled as C++.

Known issues

  • Only works with simple code samples, for now.
  • Very few functions from the Go standard library are implemented. The ideal would be to be able to compile the official Go standard library.
  • A good plan for how to implement import is needed.

Features and limitations

  • Pretty fast.
  • Simple to use.
  • Few dependencies (for compiling go2cpp, only the go compiler is needed).
  • Low complexity.
  • Short source code.

Required dependencies

  • g++ with support for C++20 is used for compiling the generated C++ code.
  • clang-format is used for formatting the generated C++ code.

Installation

go install github.com/xyproto/go2cpp@latest

Then ~/go/bin/go2cpp should be available (unless GOPATH points somewhere else).

Usage

Compile to executable:

go2cpp main.go -o main

Output what the intermediate C++20 code looks like:

go2cpp main.go

Example transformations

Go input:

// Multiple return
package main

import (
    "fmt"
)

func addsub(x int) (a, b int) {
    return x + 2, x - 2
}

func main() {
    y, z := addsub(4)
    fmt.Println("y =", y)
    fmt.Println("z =", z)
}

C++ output:

#include <iostream>
#include <tuple>

// Multiple return

auto addsub(int x) -> std::tuple<int, int>
{
    return std::tuple<int, int>{ x + 2, x - 2 };
}

auto main() -> int
{
    auto [y, z] = addsub(4);
    std::cout << "y ="
              << " " << y << std::endl;
    std::cout << "z ="
              << " " << z << std::endl;
    return 0;
}

Go input:

package main

import (
    "fmt"
)

func main() {
    m := map[string]string{"first": "hi", "second": "you", "third": "there"}
    first := true
    for k, v := range m {
        if first {
            first = false
        } else {
            fmt.Print(" ")
        }
        fmt.Print(k + v)
    }
    fmt.Println()
}

C++ output:

#include <iostream>
#include <string>
#include <unordered_map>

template <typename T> void _format_output(std::ostream& out, T x)
{
    if constexpr (std::is_same<T, bool>::value) {
        out << std::boolalpha << x << std::noboolalpha;
    } else if constexpr (std::is_integral<T>::value) {
        out << static_cast<int>(x);
    } else {
        out << x;
    }
}

auto main() -> int
{
    std::unordered_map<std::string, std::string> m{ { "first", "hi" }, { "second", "you" },
        { "third", "there" } };
    auto first = true;
    for (const auto& [k, v] : m) {
        if (first) {
            first = false;
        } else {
            std::cout << " ";
        }

        _format_output(std::cout, k + v);
    }

    std::cout << std::endl;
    return 0;
}

General info

  • Version: 0.4.0
  • License: MIT

TODO

Syntactic elements

  • backtick quoted strings: ` (one level deep only)
  • iota

Keywords

  • break
  • case
  • chan
  • const
  • continue
  • default
  • defer
  • else
  • fallthrough
  • for
  • func
  • go
  • goto
  • if
  • import (partially)
  • interface
  • map (needs more testing)
  • package (partially)
  • range
  • return
  • select
  • struct (needs more testing)
  • switch
  • type (needs more testing)
  • var

Standard library

  • fmt.Println
  • fmt.Print
  • fmt.Printf (partially)
  • fmt.Sprintf
  • strings.Contains
  • strings.HasPrefix
  • strings.HasSuffix
  • strings.Index
  • strings.Join
  • strings.NewReader
  • strings.Replace
  • strings.Split
  • strings.SplitN
  • strings.TrimSpace
  • All the rest

One goal is that all code in the standard library should transpile correctly to C++20.