-
Notifications
You must be signed in to change notification settings - Fork 2
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
Reusing nim method
for wrapping C++ methods
#1
Comments
the following "almost works": when true:
{.emit: """/*TYPESECTION*/
struct Base {
int baseField;
int baseMethod() { return 12; }
virtual int toOverride() { return 42; }
};
struct Derived : public Base {
int derivedField;
int toOverride() override { return 45; }
};
""".}
type
CxxBase {.importcpp: "Base", bycopy, inheritable.} = object
baseField: cint
CxxDerived {.importcpp: "Derived", bycopy.} = object of CxxBase
derivedField: cint
proc baseMethodImpl(base: CxxBase): cint {.importcpp: "#.baseMethod(@)".}
method baseMethod(base: CxxBase): cint {.base.} = baseMethodImpl(base)
proc toOverrideImpl(base: CxxBase): cint {.importcpp: "#.toOverride(@)".}
method toOverride(base: CxxBase): cint {.base.} = toOverrideImpl(base)
when defined case2b:
proc toOverrideImpl2(base: CxxDerived): cint {.importcpp: "#.toOverride(@)".}
method toOverride(base: CxxDerived): cint = toOverrideImpl2(base)
block:
let derived = CxxDerived(baseField: 123123)
echo derived.baseField
echo CxxBase().baseMethod()
echo CxxDerived().baseMethod()
echo CxxBase().toOverride()
when defined case2b:
echo CxxDerived().toOverride()
|
This issue can be partially solved if only {.emit: """/*TYPESECTION*/
struct Base {
int baseField;
int baseMethod() { return 12; }
virtual int toOverride() { return 42; }
};
struct Derived : public Base {
int derivedField;
int toOverride() override { return derivedField; }
};
struct Other {};
""".}
type
CxxBase {.importcpp: "Base", bycopy, inheritable.} = object
baseField: cint
CxxDerived {.importcpp: "Derived", bycopy.} = object of CxxBase
derivedField: cint
CxxOther {.importcpp: "Other", bycopy.} = object
proc aux() {.header: "<new>", importc: "//".}
proc toOverride(base: ref CxxBase): cint {.importcpp: "#->toOverride(@)".}
# {.emit: "return `base`->toOverride();".}
proc newCxxBase(): ref CxxBase =
aux()
new(result)
{.emit: "new ((void*)result) Base();".}
proc newCxxDerived(): ref CxxDerived =
aux()
new(result)
{.emit: "new ((void*)result) Derived();".}
proc newCxxOther(): ref CxxOther =
aux()
new(result)
{.emit: "new ((void*)result) Other();".}
echo newCxxBase().toOverride()
echo newCxxDerived().toOverride()
# echo newCxxOther().toOverride() |
Actually I just realized I can drop {.emit: """/*TYPESECTION*/
struct Base {
int baseField;
int baseMethod() { return 12; }
virtual int toOverride() { return 42; }
};
struct Derived : public Base {
int derivedField;
int toOverride() override { return derivedField; }
};
struct Other {};
""".}
type
CxxBase {.importcpp: "Base", byref, inheritable.} = object
baseField: cint
CxxDerived {.importcpp: "Derived", byref.} = object of CxxBase
derivedField: cint
CxxOther {.importcpp: "Other", byref.} = object
proc aux() {.header: "<new>", importc: "//".}
method toOverride(base: CxxBase): cint {.base.} =
{.emit: "return `base`->toOverride();".}
echo CxxBase().toOverride()
echo CxxDerived().toOverride()
# echo newCxxOther().toOverride() now works corectly |
cool! needs a bit of cleanup, eg remove aux and add var a = CxxDerived()
a.derivedField = 123
assert a.toOverride() == 123 |
Feel free to add it to any manual section, but after releasing alpha version of hcparse I wanted to spend some time summarizing all my solution in a series of articles, something like "Nim for C++ programmer". Also, I'm not really sure I'd this is the correct approach, so I'd prefer to mass-test it on some library (like Qt, which was the reason for this question) and then say "yes, this is the correct way that would not bite you on some edge case" |
template fn =
method toOverride(base: CxxBase): cint {.base.} =
{.emit: "return `base`->toOverride();".} # needs emit + array syntax for this to work currently but in the meantime it's good to show what's possible to unblock ppl that need interop; it's more of a living document given that C++ interop is still in flux
type
CxxDerived2 = object of CxxDerived
derivedField2: cint
method toOverride(base: CxxDerived2): cint = 33 ? |
method toOverride(derived: CxxDerived2): cint =
echo "Overide method impementation from nim side"
return 1231.cint
echo newCxxDerived2().toOverride() does not work as it fails with C++ codegen compilation error /mnt/workspace/clean-clone/hack/testing/nim/c_interop/cpp_tests/cache/@minheritance.nim.cpp:330:31: error: ‘struct Base’ has no member named ‘m_type’
330 | if (!((*base).m_type == (&NTI__5WdQBNAaXBHGF2AaoSDNJQ_))) goto LA3_; |
i see, so it's same error as above (#1 (comment)) maybe file an issue in nim repo? RFC or not RFC, this should be fixed (and bugs with a tracking issue are more likely to be addressed, at very least gives a centralized placeholder for discussion) |
Well, the issue seems obvious - object is not derived from a So I will refrain from filing issues on this since it would be incoherent noise IMO, this need to be addressed in an RFC |
there should be a solution for this case that doesn't require solving MI (which is more complex and can be addressed seperately), ie allowing (possibly with a pragma) (and yes, an RFC would be good) |
- CHANGED :: - Class wrappers now include wrappers for the superclass methods as well. Current implementation simply copy-pastes all wrapped types once more, but ideally this should be optimized, either using #1 with `{.byref.}` hack, or by a proper language support.
Alternatively to hacks (generating byref types or bruteforce solutions with copypasting every method) I can just require explicit type conversion using 'as' operator - var slider = cnewQSlidwe()
slider.as(QWidget)[].someQWidetMethod() |
When interfacing with C++ classes and methods, it is necessary to re-wrap methods as procedures for each derived class. E.g. if I have
Base
andDerived
I would have to repeat wrapping of theBase
methods forDerived
type. I tried usingmethod
, but it gives me'invalid pragma: importcpp: "#.toOverride(@)"'
error on the following code, so it looks like I can't benefit from nim OOP features in this case.I specifically repeated the implementation in nim as well to illustrate how I would like wrapper to behave. It is not difficult to generate additional wrappers for all derived classes, but as indicated in comment it could lead to multi-thousand repetitions (again
QWidget
has 37 derived classes, and it would repeat at least 214 methods for each of them. And I usedQWidget
as and example. WithQObject
it could be even bigger).I thought about possible solutions, but they don't seem particularly plausible to me:
proc toOverride(base: CxxBase | CxxDerived): cint {.importcpp: "#.toOverride(@)".}
. It might look fine for such a small example, but in the end it would mean putting all derived class wrappers in a single file, and declare all procedures afterwards, creating one giant file from a library instead of keeping things separated.{.emit.}
with macro code and traverse full inheritance tree on each call encountered in code.So the question is - can
method
somehow be used to solve this issue, or I need to generate repeated wrappers for each derived class (right now I'm doing this, which results in tons of repetitions)?Originally asked in forum thread, but also copied here because it is particularly important, but unlikely to be answered soon.
The text was updated successfully, but these errors were encountered: