Creating Library problem [SOLVED]
-
On 08/09/2014 at 07:14, xxxxxxxx wrote:
User Information:
Cinema 4D Version: 13+
Platform: Windows ; Mac ; Mac OSX ;
Language(s) : C++ ;---------
Hi,I have created a class library as described in the docs. It seems to work, the library is correctly registered, an external library call even correctly jumps into the correct member function of the original library module (verified via debugging and via debug print outs). BUT the data does not seem to be valid (or corrupted).
One of the returned data is of simple integer type and returns how many objects of a certain type is available in the current document. I print out this value from within the library module itself as well as when my other project triggers the corresponding call. The library module returns 1 (correct) but the external call to the library returns 0 every time.
Trying to retrieve a class data member (instead of a simple integer type) even results in a crash (I created another class library for that custom type as well). It seems the pointer is not valid (but how could it jump into the correct member function then?)
What could cause these non-valid data? Did I do something wrong? Has anybody successfully tried this? Is the namespace a problem?
Thanks
Here is some pseudo code:
//Hook that needs to be exposed class MyHook : public SceneHookData { private: int value; public: int get_value() const { return value; } //Correctly jumping in here but value returns 0 although it should be 1 } Bool RegisterHookLib() { ClearMem(&hooklib, sizeof(hooklib)); hooklib.get_value = &MyHook::get_value; return InstallLibrary(LIBID, &hooklib, 1000, sizeof(hooklib)); } //In the library file namespace TESTLIB { class MyHook_mirror : public BaseSceneHook { private: MyHook_mirror(); ~MyHook_mirror(); public: int get_value() const //This implementation is actually in a cpp { MyHookLib *lib = CheckHookLib(LIBOFFSET(MyHookLib , get_value)); if (!lib || !lib->get_value) return NULL; return (((MyHook* )this)->*(lib->get_value))(); } }; //INTERNAL struct MyHookLib : public C4DLibrary { int (MyHook::*get_value)() const; } }
Btw. this is how I call this:
MyHook_mirror* hookihook = static_cast<MyHook_mirror*>(doc->FindSceneHook(HOOK_ID)); if(!hookihook || hookihook->GetType() != HOOK_ID){ GePrint("Hook not found");} else GePrint(LongToString(hookihook->get_value())); //this get_value call correctly jumps into MyHook::get_value()!
-
On 08/09/2014 at 08:04, xxxxxxxx wrote:
MyHookLib *lib , does this exist? or NULL
-
On 08/09/2014 at 08:06, xxxxxxxx wrote:
As I wrote it is correctly registered and exists. Nothing is NULL, I do get filled pointers and the lib correctly points to the member functions. If it was NULL it wouldn't even get to the point of jumping into the mirrored function. If it was only that simple
-
On 08/09/2014 at 08:17, xxxxxxxx wrote:
Namespace doesn't seem to have any effect (just tried putting it into the global namespace...same outcome).
Btw. this is how I call this:
MyHook_mirror* hookihook = static_cast<MyHook_mirror*>(doc->FindSceneHook(HOOK_ID)); if(!hookihook || hookihook->GetType() != HOOK_ID){ GePrint("Hook not found");} else GePrint(LongToString(hookihook->get_value())); //this get_value call correctly jumps into MyHook::get_value()!
-
On 08/09/2014 at 08:30, xxxxxxxx wrote:
I'm imagining the problem, so what I will say may be wrong, but may lead you to the correct solution
the problem lies in: you didn't create an instance of the class, you just instanced the function (that's why you can call it, the function is returning some "relative" address to it , not sure if it will return non zero values in release mode (confirm it))
try to change it to be:
//Hook that needs to be exposed class MyHook : public SceneHookData { private: int value; public: int get_value() const { return value; } //Correctly jumping in here but value returns 0 although it should be 1 } Bool RegisterHookLib() { ClearMem(&hooklib, sizeof(hooklib)); hooklib.mHook = &MyHook; //here we get the class return InstallLibrary(LIBID, &hooklib, 1000, sizeof(hooklib)); } //In the library file namespace TESTLIB { class MyHook_mirror : public BaseSceneHook { private: MyHook_mirror(); ~MyHook_mirror(); public: int get_value() const //This implementation is actually in a cpp { MyHookLib \*lib = CheckHookLib(LIBOFFSET(MyHookLib , mHook)); //now this should get the class if (!lib || !lib->mHook) return NULL; return ((MyHookLib\* )this)->(lib->mHook.get_value()); //not sure about this line } }; //INTERNAL struct MyHookLib : public C4DLibrary { MyHook mHook; } }
-
On 08/09/2014 at 08:49, xxxxxxxx wrote:
No I cannot create an instance of MyHook. MyHook is NOT visible to the user (that's the whole point of the library and mirroring of the class. Otherwise I wouldn't need a library in the first place). Also it makes no real sense as I want the available hook to be accessed. The C4DLibrary structure works by providing function pointers. Check out the SDK docs (and the libraries in the C4D API)
-
On 08/09/2014 at 09:04, xxxxxxxx wrote:
Is maybe a wrapper class (as shown in the sdk example) mandatory? Does anybody know? Is the sdk support somewhere?
-
On 08/09/2014 at 09:28, xxxxxxxx wrote:
Well, apparently something is wrong, otherwise it would work I assume. But I cannot figure out what it is. Sigh...what am I missing here..
-
On 08/09/2014 at 09:32, xxxxxxxx wrote:
yea I told you it may be wrong, but the idea is you need a class!!
I have read the example in the SDK help, you did a mistake!! , you didn't allocate an instance of your class
class iMy1DVector;
struct My1DVectorLib : public C4DLibrary
{
Float (iMy1DVector::*GetX)() const;
void (iMy1DVector::*SetX)(Float x);
iMy1DVector* (*Alloc)();
void (*Free)(iMy1DVector*& p);
};you will need to create an instance of your class no matter what, as I told you before you have a function without a class, so it will not return the class member
-
On 08/09/2014 at 09:53, xxxxxxxx wrote:
This is not an instance allocation but a forward declaration of the class.
And no, a scene hook cannot be allocated (it itself has no allocation functions) because it can only be available once! And this instance I get via FindSceneHook. The function pointers should then call the according member functions of that instance. They are just pointers in the end.
-
On 08/09/2014 at 09:58, xxxxxxxx wrote:
you got me wrong, I didn't mean the first line "which is a forward declaration I know"
I meant the last 2 lines, Alloc function and Free function, they simply create an instance of the class "which you should create first", after that you can call the function pointer to get the member data of that allocated class -
On 08/09/2014 at 10:04, xxxxxxxx wrote:
A scene hook cannot be allocated!!! It is only allocated once by Cinema 4D once my module is registered. The call itself comes from the outside (hence the library).
And even if it was an allocateable class, if I didn't want the user to be able to create an instance of it on his own, I wouldn't need to include it. And that's the case (and no, these two do not allocate an instance, they allow the user to allocate an instance on his own and call the according alloc/free functions of the exposed class). Still, in my understanding this shouldn't change if or how the function pointers operate; which operate on the instance received by FindSceneHook (THAT points to the instance...owned by C4D).
-
On 08/09/2014 at 10:15, xxxxxxxx wrote:
I've created a working version of the example in the SDK for both a class library.
It works fine. And it returns the correct values. But I'm also not using a scene hook to make it work.My custom library gets placed in the plugins folder.
And to use it I do this in the plugin that uses the library:AutoAlloc<MyVector> myVect; //Values for the library's methods are set here locally in the plugin using the library's class myVect->SetX(5.0); myVect->SetY(25.0); myVect->SetZ(0.0); Real vX = myVect->GetX(); Real vY = myVect->GetY(); Real vZ = myVect->GetZ(); GePrint( RealToString(vX) + " " + RealToString(vY) + " " + RealToString(vZ) );
If you just want a working version of the example in the SDK. I can send it to you if you like.
But if you're trying to do something different. Like using a scene hook to use your custom library. Then I can't help.-ScottA
-
On 08/09/2014 at 10:17, xxxxxxxx wrote:
Hi Scott,
I already did that example (it was the first I started with) and it worked for me as well. Unfortunately my Hook example doesn't (it's kind of a different situation, where I want to access data that is already available and allocated by C4D internally, while the SDK example is just a data structure class that can (and is supposed to) be allocated anywhere). Thanks anyway.
-
On 08/09/2014 at 10:28, xxxxxxxx wrote:
well I understand your point, but what I sense is that you should have an instance of the class somewhere, so:
myclassFunction() {myclass mC; /* .....some stuff */ ; return mC.privateMember;}
other than that, when doing something similar in a DLL for an API, just giving the user a function is fine as long as this function will create its own work
other than that I think it is out of my knowledge
-
On 08/09/2014 at 10:45, xxxxxxxx wrote:
The class is instanced! The scene hook "MyHook" is implemented in my plugin (and the library from above as well), compiled as: myhook.cdl64.
So whenever a user opens a scene in C4D an instance of that hook is allocated by Cinema 4D! This instance is retrieved by using "FindSceneHook" as shown above.The purpose however is that the user can load the library headers into his own c4d plugin project (for example compiling into: myplugin.cdl64). He should then also be able to use FindSceneHook (see my first posting, second code) and cast it to MyHook_mirror* (That's also how the Thinking Particles library does it btw to retrieve the TP_MasterSystem Scene hook...check out the docs for TP_MasterSystem).
When he then calls the function MyHook_mirror::get_value in his plugin project, the function pointer will instead "jump" to the original member function of that hook provided by the library (myhook.cdl64), so granting him outside access to the other dll.
-
On 08/09/2014 at 10:59, xxxxxxxx wrote:
ops, well sorry for my previous posts then
-
On 08/09/2014 at 11:26, xxxxxxxx wrote:
No problem. It's good to discuss this stuff (also for me it's a proof reading of my own understanding...and apparently I am not right about something). Still hopefully someone out there can shed some light on what I am doing wrong or what I am missing or even what I got wrong here?
-
On 09/09/2014 at 08:22, xxxxxxxx wrote:
Hi Katachi, the following works for me (tested on Mac R15 only).
You can download the four files from here.main.cpp
/* Copyright (C) 2014 Niklas Rosenstein * All rights reserved. */ #include "c4d.h" #include "library.h" extern Bool RegisterTestHook(); class TestCommand : public CommandData { public: virtual Bool Execute(BaseDocument* doc) { TestSceneHook* hook = TestSceneHook::Get(doc); if (!hook) { GePrint("No TestSceneHook found in the document."); return true; } Int value = hook->GetValue(); GePrint("TestSceneHook::GetValue() returned " + String::IntToString(value)); return true; } }; Bool PluginStart() { if (!RegisterTestHook()) GePrint("Failed to register Test Scene Hook."); if (!RegisterCommandPlugin( 1000009, "Test Scene Hook Command", 0, nullptr, "", NewObj(TestCommand))) { GePrint("Failed to register Test Scene Hook Command."); } return true; } Bool PluginMessage(Int32 type, void* pData) { switch (type) { case C4DPL_INIT_SYS: return ::resource.Init(); }; return true; } void PluginEnd() { }
library.h
/* Copyright (C) 2014 Niklas Rosenstein * All rights reserved. */ #pragma once #include "c4d.h" static const Int32 ID_LIBRARY_TEST = 1000005; // Test ID static const Int32 ID_SCENEHOOK_TEST = 1000007; // Test ID struct TestLibrary : public C4DLibrary { Int (*TestSceneHook_GetValue)(const BaseSceneHook* ); }; TestLibrary* GetTestLibrary(Int offset); #ifdef TESTLIB_CALL_MACROS #define CallTestLibR(func) \n TestLibrary* _lib_ = GetTestLibrary(LIBOFFSET(TestLibrary, func)); \n if (_lib_ && _lib_->func) \n return _lib_->func #endif class TestSceneHook : public BaseSceneHook { TestSceneHook(); ~TestSceneHook(); public: Int GetValue() const; static TestSceneHook* Get(BaseDocument* doc); };
library.cpp
/* Copyright (C) 2014 Niklas Rosenstein * All rights reserved. */ #define TESTLIB_CALL_MACROS #include "library.h" TestLibrary* GetTestLibrary(Int offset) { static C4DLibrary* cache = nullptr; return static_cast<TestLibrary*>( CheckLib(ID_LIBRARY_TEST, offset, &cache)); } Int TestSceneHook::GetValue() const { CallTestLibR(TestSceneHook_GetValue)(this); return -1; } TestSceneHook* TestSceneHook::Get(BaseDocument* doc) { return static_cast<TestSceneHook*>(doc->FindSceneHook(ID_SCENEHOOK_TEST)); }
internal.cpp
/* Copyright (C) 2014 Niklas Rosenstein * All rights reserved. */ #include "c4d.h" #include "library.h" class TestSceneHookData : public SceneHookData { Int m_value; public: static NodeData* Alloc() { return NewObj(TestSceneHookData); } TestSceneHookData() : SceneHookData(), m_value(42) { } Int GetValue() const { return m_value; } }; Int TestSceneHook_GetValue(const BaseSceneHook* hook) { TestSceneHookData* data = static_cast<TestSceneHookData*>(hook->GetNodeData()); if (!data) return -1; return data->GetValue(); } Bool RegisterTestHook() { static TestLibrary lib; ClearMem(&lib, sizeof(lib)); lib.TestSceneHook_GetValue = &TestSceneHook_GetValue; Bool success = InstallLibrary(ID_LIBRARY_TEST, &lib, 1000, sizeof(lib)); if (!success) return false; success = RegisterSceneHookPlugin( ID_SCENEHOOK_TEST, "Test Scene Hook", 0, TestSceneHookData::Alloc, 0, 0, nullptr); return success; }
Best,
-Niklas -
On 09/09/2014 at 12:52, xxxxxxxx wrote:
Niklas, using a function layer taking the hook directly...thanks! So obvious but it was so hidden to me. You made my day! Appreciate the help, this gets me going.