Skip to content

[Question] Is there a way to call delegating constructor? #409

Description

@thedmd

I'm plaing with cppfront trying to port some bits of code to get a feel of new syntax.

One thing I cannot figure out is how to write operator=: to delegate construction.
I tried operator=: (out this, major: u32) = this(major, 0, 0, 0);, operator=: (out this, major: u32) = this := (major, 0, 0, 0);, even stared with operator=: (out this, major: u32) = Version(major, 0, 0, 0);.

In cppfront source I didn't found code emitting, but I'm not yet 100% sure.

Did I missed the intended syntax or feature is not present at this time? Thanks!

For reference:

Version: @value type =
{
    Major:      u32 = 0;
    Minor:      u32 = 0;
    Revision:   u32 = 0;
    Patch:      u32 = 0;

    operator=: (out this, major: u32)                                        = { this.Major = major; }
    operator=: (out this, major: u32, minor: u32)                            = { this.Major = major; this.Minor = minor; }
    operator=: (out this, major: u32, minor: u32, revision: u32)             = { this.Major = major; this.Minor = minor; this.Revision = revision; }
    operator=: (out this, major: u32, minor: u32, revision: u32, patch: u32) =
    {
        this.Major    = major;
        this.Minor    = minor;
        this.Revision = revision;
        this.Patch    = patch;
    }

Activity

  1. JohelEGP commented on May 3, 2023

    @JohelEGP
    Contributor

    It seems there's no way.

    IIRC, you should also be able to delegate to any out parameter: https://cpp2.godbolt.org/z/TGWP6fKWY.

    f: (out x: t) = { x = 0; }
    t: type = {
      a: i32;
      operator=: (out this, b: i32) = { a = b; }
      operator=: (out this, c: std::string) = {
        f(this);
      }
    }
    main: () -> int = t("").a;
    
    main.cpp2(6,5): error: expected 'a = ...' initialization statement - an operator= body must start with a series of 'member = value;' initialization statements for each of the type-scope objects in the same order they are declared
    
  2. realgdman commented on May 3, 2023

    @realgdman

    Well calling directly seems to work

    Version: @value type = {
    	Major: u32 = 0;
    	Minor: u32 = 0;
    	operator=: (out this, major: u32) = Major = major;
    	operator=: (out this, major: u32, minor: u32) = { 
    		operator=(major); 
    		Minor = minor; 
    	}
    	print: (this) = std::cout << "(Major)$.(Minor)$\n";
    }
    
    main: () = {
    	v1: Version = (11);
    	v2: Version = (20, 23);
    	
    	v1.print(); //11.0
    	v2.print(); //20.23
    }
    

    However technically thats not delegated constructor, but calling assign operator (which I think provided according to that diagram from wiki

  3. JohelEGP commented on May 3, 2023

    @JohelEGP
    Contributor

    Interesting. I tried with explicit this, but it didn't occur to me to omit it.

  4. realgdman commented on May 3, 2023

    @realgdman

    Btw is there syntax for initialization like cpp1 Ctor(a, b): a(a), b(b) ?

  5. JohelEGP commented on May 3, 2023

    @JohelEGP
    Contributor
  6. realgdman commented on May 3, 2023

    @realgdman

    Probably there is no need for delegating constructors at all and this can be solved in the future with just one constructor, default values, and named parameters in.
    Like, v:= Version(.major = 5, .minor = 3)

  7. JohelEGP commented on May 3, 2023

    @JohelEGP
    Contributor

    I think it's an accident that #409 (comment) works.
    If you remove Major's member initializer, it still breaks (https://cpp2.godbolt.org/z/8jzdnWEGq).
    That's why #409 (comment) doesn't wok with operator=(0); (https://cpp2.godbolt.org/z/zjzv5vvsv).

  8. realgdman commented on May 3, 2023

    @realgdman

    Well I think it works but not exactly how it is expected.
    Ideally it should automatically detect that initialization happens in all code paths.
    So now it expecting straightforward either in declaration i : int = 0 or inside constructor i = 0
    But in case of calling operator= it is not tracking that member was initialized inside,so initializing in delcaration suppress error, but constructor overwrites value later.
    I think there is some overhead but it is allowed to work. It's like this code

    S: type = {
    	i: int = 2;
    	operator=: (out this) = { i = 3; }
    	print: (this) = std::cout << i;
    }
    

    Which boils down to i=2; i=3;

    Edit: in other words, it's not forbidden to initialize same member different times with different values. May be thats issue, may be not.

  9. JohelEGP commented on May 3, 2023

    @JohelEGP
    Contributor

    Which boils down to i=2; i=3;

    Looking closely, that (double assignment) seems to be the case
    for the Version code that explicitly calls operator=.

  10. realgdman commented on May 3, 2023

    @realgdman

    Another exaggerated example for your #409 (comment)

    S: type = {
    	i: int; //compiles when change to = 0;
    	init: (inout this) = i = 42;
    	operator=: (out this) = init();
    }
    

    cppfront doesn't track data flow that i will be init inside function
    (Also I don't know c++ well, may be its bad to call member function before constructor is finished...)

  11. realgdman commented on May 3, 2023

    @realgdman

    In wiki it's stated that cpp2 guarantees initialization, that means initializing at least once, but it's not clear if that means exactly once and not more.

  12. JohelEGP commented on May 3, 2023

    @JohelEGP
    Contributor

    (Also I don't know c++ well, may be its bad to call member function before constructor is finished...)

    It's OK to.

  13. JohelEGP commented on May 3, 2023

    @JohelEGP
    Contributor

    I think assignment to this should delegate.

    Also, something should be decided about operator=.
    In Cpp2,

    operator= is the name for all value-setting functions
    -- https://github.com/hsutter/cppfront/wiki/Cpp2:-operator=,-this-&-that

    But currently, it lowers to Cpp1 as operator=.

  14. thedmd commented on May 4, 2023

    @thedmd
    Author

    Thanks for exploring the issue.

    My understanding is that delegating constructors are not supported right now, but can be introduced in the future. Is that correct?

    Should I edit this issue to match suggestion template?


    Few thoughts that came to me while reading the thread:

    #409 (comment)

    Cpp2 encourage use of overloading while default values for arguments are not supported. Default arguments would fit nicely in the scenario with Version type.

    #409 (comment)

    Calling directly does work, but also reverse logic where most general constructors chain calls to more specialized. It feel more like workaround due to initialization code being more convoluted in the end.

    #409 (comment)

    Double assignment here is ok'is. Feel like compromise.
    With complex types involved this can easily became no-fly zone.

  15. Repository owner locked and limited conversation to collaborators on Aug 30, 2023
  16. converted this issue into a discussion #639 on Aug 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions