Creating Libraries

A library can provide functionality to other Cinema 4D plugins. Through a library it is possible to keep compiled code in a module and let the other plugins access it through a custom header file. There are safeguards that make sure that the users get nullptr pointers if a library is not installed.

Creating a Function Library

Function libraries are the easiest to create. They only let the user call functions in a library and do not provide any classes. The first step is to create internal function. Just create a global function in the library module, for instance:

Bool iMyLibraryFunction(Int32 v, String s)
{
GePrint("v: " + String::IntToString(v) + ", s: " + s);
return v != 42;
}

Note the naming convention with an i in front. Then create the library structure. This should be a structure derived from C4DLibrary with a function pointer to the kind of function. The struct should be placed in the public header file of the library and included into the library module:

struct MyFunctionLib : public C4DLibrary
{
Bool (*MyLibraryFunction)(Int32 v, String s);
};

Creating a function pointer from a function is really only adding two parentheses and a * like above. Next create a global instance of this struct in the library cpp and write the registration function like this:

MyFunctionLib flib;
Bool RegisterMyFunctionLib()
{
// Clear the structure
ClearMem(&flib, sizeof(flib));
// Fill in all function pointers
flib.MyLibraryFunction = iMyLibraryFunction;
// Install the library
return InstallLibrary(MY_UNIQUE_FUNCTION_LIBRARY_ID, &flib, 1000, sizeof(flib));
}
Note
With the C4DMSG_PRIORITY message tell Cinema 4D that the library needs to be registered before other plugins.

Note that a & is not needed to take the function pointer address of the iMyLibraryFunction function. Also MY_UNIQUE_FUNCTION_LIBRARY_ID has to be a unique ID from www.plugincafe.com The definition should be placed in the public header:

const Int32 MY_UNIQUE_FUNCTION_LIBRARY_ID = ...;

Now create the public glue to this library. In the public header file, add a function with the same signature as the internal one:

Bool MyLibraryFunction(Int32 v, String s);
Note
The public declaration is without the i. It was appended to the internal function because otherwise they would collide when the public header is included into the library module.

Then create a public .cpp file for the library. In it first define this helper function:

MyFunctionLib* flib_cache = nullptr;
MyFunctionLib* CheckMyFunctionLib(Int32 offset)
{
return (MyFunctionLib*)CheckLib(MY_UNIQUE_FUNCTION_LIBRARY_ID, offset, (C4DLibrary**)&flib_cache);
}

Finally implement the glue function like this:

Bool MyLibraryFunction(Int32 v, String s)
{
MyFunctionLib* flib = CheckMyFunctionLib(LIBOFFSET(MyFunctionLib, MyLibraryFunction));
if (!lfib || !flib->MyLibraryFunction) return false;
return flib->MyLibraryFunction(v, s);
}

Now anyone can import the public myfunctionlibrary.h and myfunctionlibrary.cpp files into their project. And as long as the library is installed the function MyLibraryFunction() will work just as an ordinary function.

This is the complete listing for each file:

// myfunctionlibrary.h
#include "c4d.h"
Bool MyLibraryFunction(Int32 v, String s);
// INTERNAL
const Int32 MY_UNIQUE_FUNCTION_LIBRARY_ID = ...;
struct MyFunctionLib : public C4DLibrary
{
Bool (*MyLibraryFunction)(Int32 v, String s);
};
// INTERNAL
// myfunctionlibrary.cpp
#include "myfunctionlibrary.h"
MyFunctionLib* flib_cache = nullptr;
MyFunctionLib* CheckMyFunctionLib(Int32 offset)
{
return (MyFunctionLib*)CheckLib(MY_UNIQUE_FUNCTION_LIBRARY_ID, offset, (C4DLibrary**)&flib_cache);
}
Bool MyLibraryFunction(Int32 v, String s)
{
MyFunctionLib* flib = CheckMyFunctionLib(LIBOFFSET(MyFunctionLib, MyLibraryFunction));
if (!lfib || !flib->MyLibraryFunction) return false;
return flib->MyLibraryFunction(v, s);
}
// myfunctionlibrarymodule.cpp
#include "myfunctionlibrary.h"
Bool iMyLibraryFunction(Int32 v, String s)
{
GePrint("v: " + String::IntToString(v) + ", s: " + s);
return v != 42;
}
MyFunctionLib flib;
Bool RegisterMyFunctionLib()
{
// Clear the structure
ClearMem(&flib, sizeof(flib));
// Fill in all function pointers
flib.MyLibraryFunction = iMyLibraryFunction;
// Install the library
return InstallLibrary(MY_UNIQUE_FUNCTION_LIBRARY_ID, &flib, 1000, sizeof(flib));
}

Creating a Class Library

Creating a class library is a bit more complicated than creating a function library. The goal is to make a class in the public part of the library that is memory compatible with a class in the internal part of the library. The main principle is the same, but instead of function pointers we use member function pointers.

Just as with functions the first step is to create the internal class. As an example, create a 1D wrapper class for Vector:

class iMy1DVector
{
private:
Vector v;
public:
Float GetX() const { return v.x; }
void SetX(Float x) { v.x = x; }
static iMy1DVector* Alloc() { return NewObj(iMy1DVector); }
static void Free(iMy1DVector*& p) { DeleteObj(p); }
};

The allocation functions Alloc()/Free() are declared with the correct syntax for use with AutoAlloc. Now create a library struct for it with member function pointers:

class iMy1DVector;
struct My1DVectorLib : public C4DLibrary
{
Float (iMy1DVector::*GetX)() const;
void (iMy1DVector::*SetX)(Float x);
iMy1DVector* (*Alloc)();
void (*Free)(iMy1DVector*& p);
};

Note the member pointer syntax and that we need to forward declare iMy1DVector since this is done in the public header. Now to register it:

My1DVectorLib vlib;
Bool RegisterMy1DVectorLib()
{
// Clear the structure
ClearMem(&vlib, sizeof(vlib));
// Fill in all function pointers
vlib.GetX = &iMy1DVector::GetX;
vlib.SetX = &iMy1DVector::SetX;
vlib.Alloc = iMy1DVector::Alloc;
vlib.Free = iMy1DVector::Free;
// Install the library
return InstallLibrary(MY_1D_VECTOR_LIBRARY_ID, &vlib, 1000, sizeof(vlib));
}

Just as before, create a public mirror of the class in the public header file, without the i and the private members:

class My1DVector
{
public:
Float GetX() const;
void SetX(Float x);
static My1DVector* Alloc();
static void Free(My1DVector*& p);
};

Note the change in types for the allocation functions. Now there is only the helper and the glue left:

static My1DVectorLib* vlib_cache = nullptr;
My1DVectorLib* CheckMy1DVectorLib(Int32 offset)
{
return (My1DVectorLib*)CheckLib(MY_1D_VECTOR_LIBRARY_ID, offset, (C4DLibrary**)&vlib_cache);
}
Float My1DVector::GetX() const
{
My1DVectorLib* vlib = CheckMy1DVectorLib(LIBOFFSET(My1DVectorLib, GetX));
if (!vlib || !vlib->GetX) return 0.0;
return (((iMy1DVector*)this)->*(vlib->GetX))();
}
void My1DVector::SetX(Float x)
{
My1DVectorLib* vlib = CheckMy1DVectorLib(LIBOFFSET(My1DVectorLib, SetX));
if (!vlib || !vlib->SetX) return;
(((iMy1DVector*)this)->*(vlib->SetX))(x);
}
My1DVector* My1DVector::Alloc()
{
My1DVectorLib* vlib = CheckMy1DVectorLib(LIBOFFSET(My1DVectorLib, Alloc));
if (!vlib || !vlib->Alloc) return nullptr;
return (My1DVector*)vlib->Alloc();
}
void My1DVector::Free(My1DVector*& p)
{
My1DVectorLib* vlib = CheckMy1DVectorLib(LIBOFFSET(My1DVectorLib, Free));
if (!vlib || !vlib->Free) return;
iMy1DVector* tmp = (iMy1DVector*)p;
vlib->Free(tmp);
p = nullptr;
}

Note the nasty member function invocation syntax and the small tricks required in the Alloc()/Free() functions. Now anyone can use the public class like this:

v.SetFloat(5.0);

This is the complete listing for each file:

// my1dvectorlibrary.h
#include "c4d.h"
class My1DVector
{
public:
Float GetX() const;
void SetX(Float x);
static My1DVector* Alloc();
static void Free(My1DVector*& p);
};
// INTERNAL
#define MY_1D_VECTOR_LIBRARY_ID 1022949
class iMy1DVector;
struct My1DVectorLib : public C4DLibrary
{
Float (iMy1DVector::*GetX)() const;
void (iMy1DVector::*SetX)(Float x);
iMy1DVector* (*Alloc)();
void (*Free)(iMy1DVector*& p);
};
// INTERNAL
// my1dvectorlibrary.cpp
#include "mylibrary.h"
static My1DVectorLib* vlib_cache = nullptr;
My1DVectorLib* CheckMy1DVectorLib(Int32 offset)
{
return (My1DVectorLib*)CheckLib(MY_1D_VECTOR_LIBRARY_ID, offset, (C4DLibrary**)&vlib_cache);
}
Float My1DVector::GetX() const
{
My1DVectorLib* vlib = CheckMy1DVectorLib(LIBOFFSET(My1DVectorLib, GetX));
if (!vlib || !vlib->GetX) return 0.0;
return (((iMy1DVector*)this)->*(vlib->GetX))();
}
void My1DVector::SetX(Float x)
{
My1DVectorLib* vlib = CheckMy1DVectorLib(LIBOFFSET(My1DVectorLib, SetX));
if (!vlib || !vlib->SetX) return;
(((iMy1DVector*)this)->*(vlib->SetX))(x);
}
My1DVector* My1DVector::Alloc()
{
My1DVectorLib* vlib = CheckMy1DVectorLib(LIBOFFSET(My1DVectorLib, Alloc));
if (!vlib || !vlib->Alloc) return nullptr;
return (My1DVector*)vlib->Alloc();
}
void My1DVector::Free(My1DVector*& p)
{
My1DVectorLib* vlib = CheckMy1DVectorLib(LIBOFFSET(My1DVectorLib, Free));
if (!vlib || !vlib->Free) return;
iMy1DVector* tmp = (iMy1DVector*)p;
vlib->Free(tmp);
p = nullptr;
}
// my1dvectorlibrarymodule.cpp
#include "mylibrary.h"
class iMy1DVector
{
private:
Vector v;
public:
Float GetX() const { return v.x; }
void SetX(Float x) { v.x = x; }
static iMy1DVector* Alloc() { return NewObj(iMy1DVector); }
static void Free(iMy1DVector*& p) { DeleteObj(p); }
};
My1DVectorLib vlib;
Bool RegisterMy1DVectorLib()
{
// Clear the structure
ClearMem(&vlib, sizeof(vlib));
// Fill in all function pointers
vlib.GetX = &iMy1DVector::GetX;
vlib.SetX = &iMy1DVector::SetX;
vlib.Alloc = iMy1DVector::Alloc;
vlib.Free = iMy1DVector::Free;
// Install the library
return InstallLibrary(MY_1D_VECTOR_LIBRARY_ID, &vlib, 1000, sizeof(vlib));
}