question

Serge A avatar image
0 Likes"
Serge A asked anthony.johnson edited

Constructor parameters of the custom SimpleDataType (bindConstructor?)

I need to create a SimpleDataType, whose instances may refer to a particular node in the tree, which isdoes not have SDT data. So, there is a non-default constructor in the derived class, and I'd like FlexSim bindings to able to use it to create instances.

What I tried:

1. I found a public but undocumented bindConstructor method in the FlexSim Module SDK API. After some experimenting I found that the first parameter should be a static method to be able to compile it. The code below compiles but doesn't work. On start I get a error message "FlexSim encountered an unrecoverable error and must close. Please visit answers.flexsim.com for more help with this issue." Could you please provide an example how to use this bindConstructor?

2. Use bindMethod to bind the static "constructor" method which returns a new instance of the derived type. Version 2 compiles, but there is the same error on start. I suppose that FlexSim signature parser can't recognize the derived class name as the return value.

3. Bind an instance method. Same error. And it's very strange because it uses only built-in Flexscript types, and similar bindings do work in other classes I've written.

// Flexscript wrapper for InterfaceProxy
class IProxy : public SimpleDataType {
    InterfaceProxy iface;
public:
    // default constructor only for createsdtderivative()
    IProxy() {}

    IProxy(treenode current, char *ifacename)
        : iface(current, ifacename) {}

    virtual const char* getClassFactory() override {
        return "mymodule::IProxy";
    }

    virtual void bindInterface() override {
        // 1. compiles, but error on start
        //bindConstructor(ctor, "IProxy ctor(treenode current, char *ifacename)");

        // 2. compiles, but error on start
        //bindMethod(ctor, IProxy, "IProxy ctor(treenode current, char *ifacename)", BIND_METHOD_STATIC);

        // 3. compiles, but error on start
        bindMethod(init, IProxy, "int init(treenode current, char *ifacename)", 0);
    }

    int init(treenode current, char *ifacename) {
        iface.init(current, ifacename);
        return 0;
    }

    static IProxy ctor(treenode current, char* ifacename) {
        IProxy iproxy(current, ifacename);
        return iproxy;
    }
};

The error message is not really helpful to localize the problem. Could you please provide an idiomatic example how SDTs with custom constructors are supposed to be initialized?

FlexSim 18.0.6
module sdk
· 3
5 |100000

Up to 12 attachments (including images) can be used with a maximum of 23.8 MiB each and 47.7 MiB total.

Phil BoBo avatar image Phil BoBo ♦♦ commented ·

What does "I need to create a SimpleDataType, whose instances may refer to a particular node in the tree, which isdoes not have SDT data." mean?

What are you doing?

If you want to have an SDT that refers to another node, inherit from CouplingDataType instead of SimpleDataType.

If you just want a structure that isn't bound in the tree that refers to a node, just make a class with a NodeRef member and use it. Why do you need it to inherit from SimpleDataType?

The point of inheriting SimpleDataType is to store the data on a node in the tree. If you want to use SimpleDataType, then you need to bind it to a node in the tree with SDT data. Your original premise doesn't make sense. Am I missing something?

0 Likes 0 ·
Serge A avatar image Serge A Phil BoBo ♦♦ commented ·

No, I don't need coupling (like a reference from one tree node to another). I need a temporary Flexscript object, which can be initialized in Flexscript and can call C++ methods, and that object happens to refer to a particular node. For the purpose of this topic it could have been a string or a number or some other private state.

So far I used static methods on SDTs, and they're apparently faster than FLEXSIMINTERFACE functions. In this case there is a group of methods which share the same C++ data structure. I was looking into if I may avoid re-initializing that data structure on every call. So naturally I come to the idea of putting it into an instance of the derived class. But objects of this class will not "own" the referenced subtree.

> Why do you need it to inherit from SimpleDataType?

0) call overhead of the bound methods is at least 5x smaller than FLEXSIMINTERFACE overhead, 1) bindInterface is SDT's method, and SDT::bindInterface is the only documented way to create bound methods¹, 2) SDT is the only root class in the FlexSim class hierarchy. In other words, I need a fast way to call from Flexscript to C++, and do not know a better way to do it than using SDT. If possible, I'd like to avoid tree modifications.

What I'd like to do is to provide something like:

// flexscript code:
MyClass mc = MyClass(model().find("blahblah"));
mc.doX(); // call MyClass::doX() in DLL
mc.doY(); // call MyClass::doY() in DLL
mc.doZ(); // call MyClass::doZ() in DLL<br>

You did it with Table, so it's possible. I just cannot figure out how to do that with the public SDK. So far I managed to do only this:

// flexscript code:
MyClass.doX(...); // call static method MyClass::doX()<br>

¹ P.S. I see there are also "Pointer-Based Helper Classes", not derived from SDT, but the only example of using them initializes them from bindInterfaces of an SDT. It would really help if you documented the process of binding a little bit more, when and what is called.

1 Like 1 ·
Phil BoBo avatar image Phil BoBo ♦♦ Serge A commented ·

>> You did it with Table

The Table class doesn't inherit SimpleDataType.

1 Like 1 ·
anthony.johnson avatar image
0 Likes"
anthony.johnson answered Mischa Spelt commented

There is no way right now to define a custom class that is not bound to the tree. We did it with Table because we have access to the engine code and we can do that. As Phil mentioned, Table is not bound to the tree and it does not inherit from SimpleDataType. If you define a class that is a subclass of SimpleDataType, it must be something in the tree. There is no way to create a flexscript-accessible class that is not bound to the tree. Sorry.

· 2
5 |100000

Up to 12 attachments (including images) can be used with a maximum of 23.8 MiB each and 47.7 MiB total.

Serge A avatar image Serge A commented ·

Thank you. I'll look for another solution.

0 Likes 0 ·
Mischa Spelt avatar image Mischa Spelt commented ·

Hi @anthony.johnson - is this still not possible in 19.x? I'm looking to write a 'wrapper' class to do some complicated tree digging on arbitrary flow items in the model, so being able to write something like MyFlowItem(treeNode).fooBar would be ideal.

0 Likes 0 ·
anthony.johnson avatar image
1 Like"
anthony.johnson answered anthony.johnson edited

OK, my previous answer was incorrect. Sorry for the misinformation. You can create flexscript-accessible classes that are not SDT sub-classes. To do this, do the following:

  • Declare your class in c++ with a static bindInterface() method.
class MyNonSDTClass {
                
...
static void bindInterface();
...
};
  • In a flexscript-aware SDT or ODT's bindInterface() method, bind the class:
void MySDTClass::bindInterface()
{
bindClassByName<MyNonSDTClass>("MyNonSDTClass", true);
}
  • Define the class bindInterface() method. Here some of the macros are inconsistent. Some, such as bindMethod(), add the SimpleDataType:: prefix, so you don't need to, while others don't, meaning you have to put that prefix in yourself. In any case, you should be able to bind the desired properties, methods, constructors, etc., that you need. For constructors, the C++ standard doesn't allow you to get pointers to a constructor method. Thus, I usually use an indirection, like MyNonSDTClass::construct(), that is called by the constructor.

    void MyNonSDTClass::bindInterface()
    {
    bindConstructor(&MyNonSDTClass::construct, "void construct(int param1)");
    bindTypedPropertyByName<int>("state", force_cast<void*>(&MyNonSDTClass::__getState), nullptr);
    ...
    }

The 19.2 beta uses two example Non-SDT classes, namely the "Storage" namespace/class, and the Storage.Item.State class. Although they only provide static properties, they should give you an idea of how to do it.

Also, see this post for more information on doing this.

5 |100000

Up to 12 attachments (including images) can be used with a maximum of 23.8 MiB each and 47.7 MiB total.