Creating Library problem [SOLVED]
-
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