Plugin Cafe Homepage
Forum Home Forum Home > Plugin Cafe > SDK Help
  New Posts New Posts
  FAQ FAQ  Forum Search

How to expose an object function done in C++

Author
Message
  Topic Search Topic Search
Motiva View Drop Down
Member
Member


Joined: 2016 Feb 03
Online Status: Offline
Posts: 54
Direct Link To This Post Topic: How to expose an object function done in C++
    Posted: 2016 Jul 08 at 3:03am

User Information:

Cinema 4D Version:   R17 
Platform:   Windows  ;   
Language(s):   C.O.F.F.E.E  ;  C++  ;   PYTHON  ; 

---------

I've made my own kind of object and it is working fine, now I wanna to expose a function from my object to be called from Python and Coffee.


Is there any example?

Thank you in advance.
Back to Top
Cactus Dan View Drop Down
Member
Member
Avatar

Joined: 2003 Apr 18
Location: United States
Online Status: Offline
Posts: 1053
Direct Link To This Post Posted: 2016 Jul 08 at 6:02am
Howdy,

Well, you can create Coffee functions with the SDK's Coffee class, but I don't know if you can do it for Python, as I don't see a Python class in the SDK documentation.

EDIT:
Take a look at this thread:

Adios,
Cactus Dan


Edited by Cactus Dan - 2016 Jul 08 at 6:05am
Back to Top
Motiva View Drop Down
Member
Member


Joined: 2016 Feb 03
Online Status: Offline
Posts: 54
Direct Link To This Post Posted: 2016 Jul 08 at 8:55am
Sorry I didn't explain properly I mean a function relative to my object, never used COFFEE but I mean something like 

MyObject.saveDataTo ("c:\\whatever.txt")

being MyObject a instance of an object of my own class.


Edited by Motiva - 2016 Jul 08 at 9:08am
Back to Top
Cactus Dan View Drop Down
Member
Member
Avatar

Joined: 2003 Apr 18
Location: United States
Online Status: Offline
Posts: 1053
Direct Link To This Post Posted: 2016 Jul 08 at 9:30am
Howdy,

Well, you could do something like this:
class MyObject : public ObjectData
{
public:
Bool setting;
Bool GetSetting(void);
// other class functions...
};

Bool GetSetting(void)
{
return setting;
}

// coffee function to access object setting
void MyObjectGetSetting(Coffee *cof, Value*&sp, Int32 argc)
{
Bool res = false;
if(argc == 1 && sp[argc-1].IsType(COFFEE_VTYPE_OBJECT))
{
GeData gData;
GeCoffeeValue2GeData(cof,&sp[argc-1],&gData);
BaseLink* bl = gData.GetBaseLink();
BaseObject *op = static_cast<BaseObject*>(bl->GetLink(GetActiveDocument()));
if(op)
{
MyObject *mOp = op->GetNodeData();
if(mOp) res = mOp->GetSetting();
}
}
GeCoffeeGeData2Value(cof, res, &sp[argc]);
sp += argc;
}

... and then in your coffee script call it like this:
var setng = MyObjectGetSetting(op);

Is that similar to what you want to do?

Adios,
Cactus Dan

Back to Top
Motiva View Drop Down
Member
Member


Joined: 2016 Feb 03
Online Status: Offline
Posts: 54
Direct Link To This Post Posted: 2016 Jul 08 at 9:48am
This part ...

// coffee function to access object setting
void MyObjectGetSetting(Coffee *cof, Value*&sp, Int32 argc)
{
Bool res = false;
if(argc == 1 && sp[argc-1].IsType(COFFEE_VTYPE_OBJECT))
{
GeData gData;
GeCoffeeValue2GeData(cof,&sp[argc-1],&gData);
BaseLink* bl = gData.GetBaseLink();
BaseObject *op = static_cast<BaseObject*>(bl->GetLink(GetActiveDocument()));
if(op)
{
MyObject *mOp = op->GetNodeData();
if(mOp) res = mOp->GetSetting();
}
}
GeCoffeeGeData2Value(cof, res, &sp[argc]);
sp += argc;
}

...is inside my object (C++) or this is pure COFFEE?

Thank you so much
Back to Top
Cactus Dan View Drop Down
Member
Member
Avatar

Joined: 2003 Apr 18
Location: United States
Online Status: Offline
Posts: 1053
Direct Link To This Post Posted: 2016 Jul 08 at 10:03am
Howdy,

That would be a function outside of your object class. You can add it to the bottom of your myobject.cpp file, and declare it in the main header file.

Then in your PluginStart() function in your main.cpp file, you add your function to the coffee master like this:
Coffee *cofM = GetCoffeeMaster();
if(cofM)
{
cofM->AddGlobalFunction("MyObjectGetSetting",MyObjectGetSetting);
}

Adios,
Cactus Dan
Back to Top
Yannick Puech View Drop Down
Forum Moderator
Forum Moderator


Joined: 2011 Apr 13
Location: Spain
Online Status: Offline
Posts: 1143
Direct Link To This Post Posted: 2016 Jul 15 at 7:13am
Hi!

Sorry for the late reply. It's possible to define new Python functions using the library defined in c4d_libs/lib_py.h
This library definitions are not documented currenly and many are meant for internal use only.

To expose a Python function a new module has to be initialized with PythonLibrary::InitModule().
Here is some code:
// Global table of c4d.extendpyapi functions for PythonLibrary::InitModule()
static maxon::BaseArray<PythonMethodData>* g_extendpyapi_functions = nullptr;

// Prints "Hello from Python!" to the console
static _PyObject *extendpyapi_HelloPython(_PyObject *self)
{
  PythonLibrary pylib;
  GePrint("Hello from Python!");
  return pylib.ReturnPyNone();
}

// Initializes c4d.extendpyapi module
void InitExtendPython()
{
  PythonLibrary pylib;

  // Allocates c4d.extendpyapi module functions
  g_extendpyapi_functions = NewObjClear(maxon::BaseArray<PythonMethodData>);
  if (g_extendpyapi_functions == nullptr)
    return;

  // Initializes c4d.extendpyapi module functions
  g_extendpyapi_functions->Resize(2);
  PythonMethodData* moduleFunctions = g_extendpyapi_functions->GetFirst();
  moduleFunctions[0].Init("HelloPython", (PyFn)extendpyapi_HelloPython, PYFN_FLAGS_NOARGS, "HelloPython() - Extend Python API");
  moduleFunctions[1].Init(String(), nullptr, PYFN_FLAGS_0, String()); // Last dummy element!

  // Initializes c4d.extendpyapi module
  if (pylib.InitModule("c4d.extendpyapi", moduleFunctions, "Extend Python API"))
    GePrint("\'c4d.extendpyapi\' module successfully initialized");
}
 
// Frees the module global function table
void FreeExtendPython()
{
  DeleteObj(g_extendpyapi_functions);
}

InitExtendPython()/FreeExtendPython() have to called from within PluginMessage() C4DPL_PYINITTYPES/C4DPL_PYFINALIZE:
Bool PluginMessage(Int32 id, void* data)
{
  case C4DPL_PYINITTYPES:
  {
    InitExtendPython();
    return true;
  }
  case C4DPL_PYFINALIZE:
  {
    FreeExtendPython();
    return true;
  }

  return false;
}

To pass basic parameters like string, integer or float use PythonLibrary::ParseTupleAndKeywords() like shown in the following code snippet:
static _PyObject *extendpyapi_PassParameters(_PyObject *self, _PyObject *args, _PyObject *keywords)
{
  PythonLibrary pylib;
 
  String str;
  Int32 integer = 0;
  Float real = 0.0f;

  const Char *kwlist[] = {"str", "integer", "real", nullptr};
  if (!pylib.ParseTupleAndKeywords(args, keywords, "$if", kwlist, &str, &integer, &real))
    return nullptr;

  if (str.Content())
    GePrint("Parameter str: " + str);

  GePrint("Parameter integer: " + String::IntToString(integer));
  GePrint("Parameter real: " + String::FloatToString(real));
 
  return pylib.ReturnPyNone();
}
If a function accepts parameters pass PYFN_FLAGS_KEYWORDS instead of PYFN_FLAGS_NOARGS for the Init() call.

Finally, to parse a GeData/baselist object use 'G' format:
static _PyObject *extendpyapi_PassBaseList(_PyObject *self, _PyObject *args, _PyObject *keywords)
{
  PythonLibrary pylib;
 
  GeData data;
 
  const Char *kwlist[] = {"baselist", nullptr};
  if (!pylib.ParseTupleAndKeywords(args, keywords, "G", kwlist, &data))
    return nullptr;
 
  if (data.GetType() == DA_ALIASLINK)
  {
    BaseLink* link = data.GetBaseLink();
    if (link == nullptr)
      return pylib.ReturnPyNone();
 
    BaseList2D* baseList = link->GetLink(nullptr);
    if (baseList == nullptr)
      return pylib.ReturnPyNone();
 
    GePrint("Passed baselist: " + baseList->GetName());
  }
 
  return pylib.ReturnPyNone();
}







Edited by Yannick Puech - 2016 Jul 15 at 7:24am
MAXON
Developer Support
Back to Top
Cactus Dan View Drop Down
Member
Member
Avatar

Joined: 2003 Apr 18
Location: United States
Online Status: Offline
Posts: 1053
Direct Link To This Post Posted: 2016 Jul 15 at 10:15am
Howdy,

Wow, thanks Yannick!

I've been working on making aspects of my plugins scriptable through cofffee and wanted to included python scriptability, too. This gives me a place to start. Wink

Adios,
Cactus Dan
Back to Top
Cactus Dan View Drop Down
Member
Member
Avatar

Joined: 2003 Apr 18
Location: United States
Online Status: Offline
Posts: 1053
Direct Link To This Post Posted: 2016 Jul 28 at 4:15pm
Howdy,

OK, I got the passing a BaseList as a prameter with the "G" format, but suppose I want to pass both a BaseList and an interger or real in the same function? How would that be formatted?

Adios,
Cactus Dan
Back to Top
Yannick Puech View Drop Down
Forum Moderator
Forum Moderator


Joined: 2011 Apr 13
Location: Spain
Online Status: Offline
Posts: 1143
Direct Link To This Post Posted: 2016 Jul 29 at 2:37am
Originally posted by Cactus Dan

OK, I got the passing a BaseList as a prameter with the "G" format, but suppose I want to pass both a BaseList and an interger or real in the same function? How would that be formatted?
Like the argument parsing in the function example extendpyapi_PassParameters() I posted above, concatenate the characters for each parameter.
For instance:
GeData data;
Int32 integer = 0;
Float real = 0.0f;

const Char *kwlist[] = {"baselist", "integer", "real", nullptr};
if (!pylib.ParseTupleAndKeywords(args, keywords, "Gif", kwlist, &data, &integer, &real))
    return nullptr;


MAXON
Developer Support
Back to Top
Cactus Dan View Drop Down
Member
Member
Avatar

Joined: 2003 Apr 18
Location: United States
Online Status: Offline
Posts: 1053
Direct Link To This Post Posted: 2016 Jul 29 at 4:53am
Howdy,

DOH! It's the strings that always throw me off when they're used for anything other than printing.
But I get it now:
"$" = string format
"i" = integer format
"f" = float format
"G" = GeData format

Possibly if the example had the float first like "$fi" then I might have been able to figure that out. But, with it the other way around, no matter how many times I looked at it, I still saw the word "if", and got confused. Embarrassed

Thanks for the clarification.

Adios,
Cactus Dan
Back to Top
Yannick Puech View Drop Down
Forum Moderator
Forum Moderator


Joined: 2011 Apr 13
Location: Spain
Online Status: Offline
Posts: 1143
Direct Link To This Post Posted: 2016 Jul 29 at 5:53am
You're welcome :)

"C" can also be used to parse BaseContainer* too (note only pointer to BaseContainer, initialize with nullptr before calling ParseTupleAndKeywords()).


MAXON
Developer Support
Back to Top
Cactus Dan View Drop Down
Member
Member
Avatar

Joined: 2003 Apr 18
Location: United States
Online Status: Offline
Posts: 1053
Direct Link To This Post Posted: 2016 Jul 29 at 6:07am
Howdy,

Thanks. Is there a complete list of formatting letters that you could post?

Also, is there a way to add global symbols in python like in coffee, and if so, can you post an example?

Adios,
Cactus Dan
Back to Top
Miro View Drop Down
Member
Member
Avatar

Joined: 2015 Feb 11
Online Status: Offline
Posts: 35
Direct Link To This Post Posted: 2017 Apr 26 at 12:05am
Hello,

I know this is a bit older topic but the last question from Cactus Dan was not answered and I also need it. Especially I need to transfer BaseObject to and from python script.

So again, we just need a table of all formatting letters and types that Cinema python binding API uses.

Just for completeness, python build-in formatting letters seems to work and are available here: https://docs.python.org/2.7/c-api/arg.html?highlight=parsetupleandkeywords#c.PyArg_ParseTupleAndKeywords


Back to Top
Yannick Puech View Drop Down
Forum Moderator
Forum Moderator


Joined: 2011 Apr 13
Location: Spain
Online Status: Offline
Posts: 1143
Direct Link To This Post Posted: 2017 Apr 26 at 2:38am
Hi Miro,

Originally posted by Miro

I know this is a bit older topic but the last question from Cactus Dan was not answered and I also need it. Especially I need to transfer BaseObject to and from python script.
To parse a BaseList based object, use 'G' format character as show in my above code snippet's function extendpyapi_PassBaseList(). The object can be casted to the most interesting type after retrieving it.

Originally posted by Miro

So again, we just need a table of all formatting letters and types that Cinema python binding API uses.
PythonLibrary::ParseTupleAndKeywords() accepts the following format characters for Cinema 4D C++ API classes:
- $: String
- %: Filename
- M: Matrix
- V: Vector
- Q: Quaternion
- C: BaseContainer*
- G: GeData (versatile: can parse a BaseList, a custom data, a time, etc.)
- T: BaseTime
- X: BaseThread

Originally posted by Miro

Just for completeness, python build-in formatting letters seems to work and are available here: https://docs.python.org/2.7/c-api/arg.html?highlight=parsetupleandkeywords#c.PyArg_ParseTupleAndKeywords
PythonLibrary::ParseTupleAndKeywords()also accepts the following standard format characters: b, B, j, h, i, I, v, l, L, r, f, d, c





Edited by Yannick Puech - 2017 Apr 26 at 2:39am
MAXON
Developer Support
Back to Top
Miro View Drop Down
Member
Member
Avatar

Joined: 2015 Feb 11
Online Status: Offline
Posts: 35
Direct Link To This Post Posted: 2017 Apr 27 at 11:34pm
That's perfect! Thank you.
Back to Top
Hermenator View Drop Down
Member
Member
Avatar

Joined: 2015 Jan 23
Location: NL
Online Status: Offline
Posts: 30
Direct Link To This Post Posted: 2017 Sep 24 at 1:07pm
Hello all,

It seems to be that i's only possible to make a python extension library as a plugin. Which plugin then is best suited? A command plugin, I would guess, or is there another plugin that loads completely automatic at startup?

I need something that is always available, so that I don't have to think about loading the library. Much like the c++ example here: page_creating_libraries.html

Regards,

Hermen
Back to Top
S_Bach View Drop Down
Forum Moderator
Forum Moderator
Avatar

Joined: 2011 Jun 27
Online Status: Offline
Posts: 1379
Direct Link To This Post Posted: 2017 Sep 25 at 1:29am
Hello,

a "plugin" is just a custom module loaded by Cinema. So a "plugin" does not need to include anything e.g. a command data extension. You only have to implement PluginStart()/PluginMessage()/PluginEnd(), see Plugin Functions Manual. In such a PluginMessage() function you have to register your Python extension as shown above.

best wishes,
Sebastian

SDK Support Engineer
Back to Top
Hermenator View Drop Down
Member
Member
Avatar

Joined: 2015 Jan 23
Location: NL
Online Status: Offline
Posts: 30
Direct Link To This Post Posted: 2017 Sep 25 at 2:53am
Aha!
Now I understand, thanks for clearing that up!

regards,

Hermen
Back to Top

Forum Jump Forum Permissions View Drop Down

Bulletin Board Software by Web Wiz Forums® version 9.61 [Free Express Edition]
Copyright ©2001-2009 Web Wiz

This page was generated in 0.105 seconds.