Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

C++ move semantics, copying, destructors, memory management #13

Open
haxscramper opened this issue Oct 7, 2021 · 6 comments
Open

C++ move semantics, copying, destructors, memory management #13

haxscramper opened this issue Oct 7, 2021 · 6 comments
Labels
C++ Issues related specifically to C++ processing open for design discussion question Further information is requested

Comments

@haxscramper
Copy link
Owner

haxscramper commented Oct 7, 2021

type
  NimObject {.exportc: "NimObject".} = object
    field: int

proc `=destroy`(obj: var NimObject) {.exportc: "destroy_NimObject".} =
  echo "Called nim destructor for ", obj.field

{.emit:"""/*TYPESECTION*/
template <typename T>
struct CxxContainer { T value; };

""".}


type
  CxxContainer[T] {.importcpp.} = object
    value: T

proc `=destroy`[T](obj: var CxxContainer[T]) =
  echo "Called destroy for cxx container"
  `=destroy`(obj.value)

# proc `=copy`(dest: var T; source: T)
proc `=copy`[T](dest: var CxxContainer[T], source: CxxContainer[T]) =
  echo "Copying cxx container"

# proc `=sink`(dest: var T; source: T)
proc `=sink`[T](dest: var CxxContainer[T], source: CxxContainer[T]) =
  echo "Moving cxx container"


proc main() =
  block:
    var c = CxxContainer[NimObject](value: NimObject(field: 2))
    var c1 = c

main()
#include <iostream>

struct NimObject {
    int field;
    ~NimObject() { std::cout << "Called destroy for " << field << "\n"; }
};

template <typename T>
struct CxxContainer { 
    T value; 
    CxxContainer(T&& _value) : value(_value) { }
        
    ~CxxContainer() { std::cout << "Called destroy for container\n"; }
    CxxContainer(CxxContainer&& other) = default;
    CxxContainer(const CxxContainer& other) { 
        value = other.value;
        std::cout << "Called copy on cxx container\n"; 
    }
};

int main() {
    CxxContainer<NimObject> c(NimObject{2});
    auto c1 = c;
}

In these two example nim and C++ code produce completely different results - nim calls destructor only once, does not invoke neither copy nor move.

    CxxContainer<NimObject> c(NimObject{2});
    auto c1 = c;
Called destroy for 2 // I'm not sure where this destroy call comes from
Called copy on cxx container
Called destroy for container
Called destroy for 2
Called destroy for container
Called destroy for 2
    var c = CxxContainer[NimObject](value: NimObject(field: 2))
    var c1 = c
Called destroy for cxx container
Called nim destructor for 2
@haxscramper haxscramper added open for design discussion question Further information is requested C++ Issues related specifically to C++ processing labels Oct 7, 2021
@haxscramper
Copy link
Owner Author

haxscramper commented Oct 7, 2021

@haxscramper
Copy link
Owner Author

Main issue that arises from this difference - I need to figure out a way how to "mend together" nim and C++ object ownership/memory-management semantics. Nim (sadly) does not have precise tools for handling objects, like "function requires rvalue", "cannot copy", "cannot default-construct" (nim-lang/RFCs#252) and the best nim counterpart for some of these cases would be just "API by convention", which would just lead users into codegen bugs.

@haxscramper
Copy link
Owner Author

Running with --expandArc:main shows that nim code infers c1 to be a cursor into c

block :tmp:
  var c
  c = CxxContainer[NimObject](value: NimObject(field: 2))
  var c1_cursor = c
  `=destroy`(c)

@haxscramper
Copy link
Owner Author

haxscramper commented Oct 24, 2021

Partially related nim-lang/RFCs#432 - if would be nice to have some form of "requires move" etc. annotations

How to prevent return value from been=copyed?

@haxscramper haxscramper changed the title C++ move semantics, copying, destructors C++ move semantics, copying, destructors, memory management Oct 25, 2021
@haxscramper
Copy link
Owner Author

  • Support automatic ref T generation for cxx classes. Was supported in IRv1, but best to be reimplemented.

Two possible ways of implementing -

proc newImportAux*() {.importc: "//", header: "<new>".} =
  discard

proc newType(arg1, arg2...) ref Type =
  new(result)
  newImportAux()
  {.emit: "new ((void*)result) Type(`arg1`, `arg2`);.}

or

proc cxxPlaceNew*[T](x: ref T) {.
  header: "<new>", importcpp: "(new (#) '*0(@))", varargs.}

proc newType(arg1, arg2...) ref Type =
  new(result)
  cxxPlaceNew(arg1, arg2)

cxxPlaceNew should be a part of helper library, or automatically generated for the wrappers (#11 can setup everything, or code can be generated anew)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
C++ Issues related specifically to C++ processing open for design discussion question Further information is requested
Projects
None yet
Development

No branches or pull requests

1 participant