Creating Library problem [SOLVED]
-
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.
-
On 09/09/2014 at 14:08, xxxxxxxx wrote:
You're welcome.
Btw, I think to make your original example work, you were indeed doing something
wrong with calling the pointer-to-member function.Especially this looks strange to me: lib->mHook.get_value()
I think the right code would be something like this:
class MyHookLib : public C4DLibrary { public: int ( **SceneHookData** ::*get_value)() const; }; class MyHookMirror : public BaseSceneHook { public: int get_value() const { SceneHookData* data = static_cast<SceneHookData*>(GetNodeData()); MyHookLib* lib = CheckLib(...); if (data && lib && lib->get_value) return **(data- >*lib->get_value)();** return -1; } }
Note that it's using a pointer-to-member function of a SceneHookData class as your own
SceneHookData subclass will not be visible to users of your library (and it should work
though!).See also: http://www.parashift.com/c++-faq-lite/pointers-to-members.html
Best,
-Niklas -
On 09/09/2014 at 14:55, xxxxxxxx wrote:
Originally posted by xxxxxxxx
You're welcome.
Btw, I think to make your original example work, you were indeed doing something
wrong with calling the pointer-to-member function.Especially this looks strange to me: lib->mHook.get_value()
return (data- >*lib->get_value)();
Thanks, but that was not my code but the changed code of Mohamed. My code is the one in the initial thread posting (and does already as you suggest)
-
On 09/09/2014 at 15:02, xxxxxxxx wrote:
Originally posted by xxxxxxxx
int ( SceneHookData ::*get_value)() const;
Note that it's using a pointer-to-member function of a SceneHookData class as your own
SceneHookData subclass will not be visible to users of your library (and it should work
though!).So you are saying that when I am using (MyHook::*get_value) instead of its base class it won't work? I will read up on this link tommorrow (unbelievably tired..), but that would be a bummer. (I mean it would be great if that was the issue, I am all for anything that fixes my problem..hehe)
My thoughts were that I can forward declare the class to use for the pointer-to-member function declaration (just as it is done with the iMy1DVector taking the SDK example as refernce). imo that should work. Really must read up on that link. This feels strange (especially as get_value is not defined in SceneHookData).. But I'll try it out.
-
On 09/09/2014 at 15:44, xxxxxxxx wrote:
Originally posted by xxxxxxxx
Originally posted by xxxxxxxx
You're welcome.
Btw, I think to make your original example work, you were indeed doing something
wrong with calling the pointer-to-member function.Especially this looks strange to me: lib->mHook.get_value()
return (data- >*lib->get_value)();
Thanks, but that was not my code but the changed code of Mohamed. My code is the one in the initial thread posting (and does already as you suggest)
Oh sorry, you are right But (((MyHook* )this)->*(lib->get_value))(); is not right either.
this is not MyHook (SceneHookData subclass) but MyHook_mirror (BaseSceneHook subclass), it
is therefore undefined to cast it into a MyHook pointer.Originally posted by xxxxxxxx
Originally posted by xxxxxxxx
int ( SceneHookData ::*get_value)() const;
Note that it's using a pointer-to-member function of a SceneHookData class as your own
SceneHookData subclass will not be visible to users of your library (and it should work
though!).So you are saying that when I am using (MyHook::*get_value) instead of its base class it won't work? I will read up on this link tommorrow (unbelievably tired..), but that would be a bummer. (I mean it would be great if that was the issue, I am all for anything that fixes my problem..hehe)
My thoughts were that I can forward declare the class to use for the pointer-to-member function declaration (just as it is done with the iMy1DVector taking the SDK example as refernce). imo that should work. Really must read up on that link. This feels strange (especially as get_value is not defined in SceneHookData).. But I'll try it out.
I'm not sure what you're trying to say, so I'll try to clarify what I meant: I think that you usually
don't expose the SceneHookData subclass declaration to the third party that uses the functionality
of your plugin provided by your C4DLibrary. The MyHook class will therefore not be defined at this
point in the code.Originally posted by xxxxxxxx
This feels strange (especially as get_value is not defined in SceneHookData)..
The syntax is confusing. For pointer-to-member functions you have to declare the type of the
object that you will call the function for. The part after :: does not need to be a member of
the class, but it is the name of the declaration (variable or typedef name).typedef int (*some_func_t)(FooClass* );
typedef int (FooClass::*some_member_func_t);some_func_t func = &SomeGlobalFunction;
some_member_func_t member_func = &FooClass::SomeFunction;Best,
-Niklas -
On 10/09/2014 at 00:47, xxxxxxxx wrote:
Hey Niklas!
Originally posted by xxxxxxxx
Oh sorry, you are right But (((MyHook* )this)->*(lib->get_value))(); is not right either.
this is not MyHook (SceneHookData subclass) but MyHook_mirror (BaseSceneHook subclass), it
is therefore undefined to cast it into a MyHook pointer.This should be a pointer to the class (http://www.parashift.com/c++-faq-lite/dotstar-vs-arrowstar.html).
Hmm, then I don't understand the libs used in the C4D API (and the SDK example for creating a library is wrong then?). For example, check out lib_batchrender:class BatchRender { private: BatchRender(); ~BatchRender(); public: Bool Open(void); }; //--------------------------------------------------- // ---INTERNAL STUFF class iBatchRender; //Forward Declaration! struct BatchRenderLibrary : public C4DLibrary { Bool (iBatchRender::\*Open) };
Here iBatchRender is forward declared and still used for defining the according member function call. It is also not visible to the third party (me in this case ) but is simply forward declared in order to make the definition. This is the classic C++ class layering to me.
and check out how the lib now does the library call:
Bool BatchRender::Open() { BatchRenderLibrary \*lib = CheckBatchRenderLib(LIBOFFSET(BatchRenderLibrary, Open)); if (!lib || !lib->Open) return FALSE; return (((iBatchRender\* )this)->\*(lib->Open))(); } It uses iBatchRender here as well (though it's not defined). To me this looks fine (as forward declared) and should still point correctly to the member function. Can you elaborate a bit more? Thanks! However, I have definetly not taken into the account the GetNodeData call as you have it. This is a potential gap in my code!! Thanks!
Originally posted by xxxxxxxx
I'm not sure what you're trying to say, so I'll try to clarify what I meant: I think that you usually
don't expose the SceneHookData subclass declaration to the third party that uses the functionality
of your plugin provided by your C4DLibrary. The MyHook class will therefore not be defined at this
point in the code.Yeah, although true, what I mean is that the definition does not need to be available at this point but only at the point when I assign the member function pointer (which happens in the internal.cpp to stay with your code example file names).
Just like with any other forward declaration of classes, as long as I don't call member functions of the pointer (etc.), I can easily use it's declaration in my code even if the class is not yet defined). As all I need here is a pointer-to-class if you want so (since all object pointers are of same size, the compiler shouldn't care).The syntax is confusing. For pointer-to-member functions you have to declare the type of the
object that you will call the function for. The part after :: does not need to be a member of
the class, but it is the name of the declaration (variable or typedef name).typedef int (*some_func_t)(FooClass* );
typedef int (FooClass::*some_member_func_t);some_func_t func = &SomeGlobalFunction;
some_member_func_t member_func = &FooClass::SomeFunction;thanks for this one, I wasn't aware of that actually (or misinterpreted that is was only a declaration name). This is still nightmare syntax and I hate C++ for it as it confuses me
-
On 10/09/2014 at 01:31, xxxxxxxx wrote:
Originally posted by xxxxxxxx
However, I have definetly not taken into the account the GetNodeData call as you have it. This is a potential gap in my code!!
Thanks!Yes, yes, yes! Thanks so much Niklas. This little thingy seems to have been the culprit!! If I change the code to (((const MyHook\* )this->GetNodeData())->\*(vlib->get_value))(); then it works!! I now get the correct value :) Of course it then works. It was stupid of me to cast directly to the SceneHookData without using GetNodeData...sigh, I need more sleep! I will change the other code of mine and see if this works for the other member functions I have as well, but you definetly deserve a beer (was trinkt man bei euch so?)! ;) I report back if everything works now as expected
-
On 10/09/2014 at 04:24, xxxxxxxx wrote:
Yep, now it all works like a charm. it was well worth the discussion!
Sigh, such a minor mistake of mine cost me 3 days...always the same with this coding