Search code examples
c++compiler-constructioncode-generation

Programming language development practice, how to compile golang style interfaces to c++?


For fun I have been working on my own programming language that compiles down to C++. While most things are fairly straightforward to print, I have been having trouble compiling my golang style interfaces to c++. In golang you don't need to explicitly declare that a particular struct implements an interface, it happens automatically if the struct has all the functions declared in the interface. Originally I was going to compile the interfaces down to a class with all virtual methods like so

class MyInterface {
    public:
        void DoSomthing() = 0;
}

and all implementing structures would simply extend from the interface like you normally would in c++

class MyClass: public MyInterface {
    // ...
}

However this would mean that my compiler would have to loop over every interface defined in the source code (and all dependencies) as well as every struct defined in the source and check if the struct implements the interface using an operation that would take O(N*M) time where N is the number of structs and M is the number of interfaces. I did some searching and stumbled upon some c++ code here: http://wall.org/~lewis/2012/07/23/go-style-interfaces-in-cpp.html that makes golang style interfaces in c++ a reality in which case I could just compile my interfaces to code similar to that (albeit not exactly since I am hesitant to use raw pointers over smart pointers) and not have to worry about explicitly implementing them. However the author states that It should not be done for production code which worries me a little.

This is kinda a loaded question that may be a little subjective, but could anyone with more C++ knowledge tell me if doing it the way suggested in the article is a really bad idea or is it actually not that bad and could be done, or if there is a better way to write c++ code that would allow me to achieve the behavior I want without resorting to the O(N*M) loop?


Solution

  • My initial thought is to make use of the fact that C++ supports multiple inheritance. Decompose your golang interface into single-function interfaces. Hash all interfaces by their unique signature. It now becomes an O(N) operation to find the set of C++ abstract interfaces for your concrete classes.

    Similarly, when you consume an object, you find all the consumed interfaces. This is now O(M) by the same logic. The total compiler complexity then becomes O(N)+O(M) instead of O(N*M).

    The slight downside is that you're going to have O(N) vtables in C++. Some of those might be merged if certain interfaces are always groupd together.