gluecodium icon indicating copy to clipboard operation
gluecodium copied to clipboard

Make custom struct's constructors in C++ consistent with the rest of code.

Open Hsilgos opened this issue 4 years ago • 1 comments

Consider next struct in lime:

struct Foo {
    field1: String
    field2: String = "predefined"

    constructor make(field1: String)
    constructor make(field1: String, field2: String)
}

Custom constructors should check parameters or fill fields by non-trivial logic. When generated code in Swift or Java contains only two constructors in this case. But C++ looks like this:

struct Foo {
    ::std::string field1;
    ::std::string field2 = "predefined";

    Foo( );
    Foo( ::std::string field1 );
    Foo( ::std::string field1, ::std::string field2 );

    static Foo make( const ::std::string& field1 );
    static Foo make( const ::std::string& field1, ::std::string field2 );
};

In C++ correct way is to use factory methods like Foo::make(...), but it's still possible to create instance with constructors and avoid parameters checking or custom logic. My idea is to make C++ consistent with Java/Swift in next way:

  1. Move custom factory methods 'make' to private sections
  2. Call factory methods in public constructors in the same way as Swift/Java does
  3. Generate constructor(s) in private section which should be called from factory methods:
struct Foo {
    ::std::string field1;
    ::std::string field2 = "predefined";
    
    // No default Foo() ctor since it's not described in lime
    Foo( ::std::string field1 );
    Foo( ::std::string field1, ::std::string field2 );

private:
    enum Custom {
       Constructor
    };

    Foo( Custom );
    Foo( Custom, ::std::string field1);
    Foo( Custom, ::std::string field1, ::std::string field2 );

    static Foo make( const ::std::string& field1 );
    static Foo make( const ::std::string& field1, ::std::string field2 );
};

So, private constructors must be the same as currently generated. Public constructors must look like

Foo::Foo( ::std::string field1 ) : Foo(make(std::move(field1))) {}
Foo::Foo( ::std::string field1, ::std::string field1 ) : Foo(make(std::move(field1), std::move(field2))) {}

Custom factory methods should create structs only with private constructors:

Foo Foo::make(::std::string field1) {
  return Foo(Foo::Custom::Constructor, field1, "some-custom-value");
}

Note: It's breaking change since existing code may produce infinite recursion calls, so may be it should be temporary optional behaviour with warning about deprecation.

Hsilgos avatar Oct 20 '20 15:10 Hsilgos

When we're converting structs from platform to C++ we need to initialize them somehow. Right now it's done in two ways:

  • with default ctor and field assignment for mutable structs
  • with all-args ctor for immutable structs. Can be changed to always use all-args ctor. But there is no way to do it for a struct with a custom constructor if we hide all non-custom constructors, as per the proposal.

DanielKamkha avatar Jun 24 '21 11:06 DanielKamkha