How to add a class structure to a Module Plugin?

I am writing a plugin for OpenGL mathematics called GLM, and I would like to make datatypes so that it can be created in Xojo with the format similar to:

Var MyDataType as var4

A var4 datatype is made of 4 singles: var4(x as Single, y as Single, z as Single, w as Single). I have been able to make a structure in a module, and it acts - like a module structure that is created at runtime and is not dynamic - as expected.

In the CompleteModule example supplied by Xojo in the PluginsSDK there are no examples on how to do this. After playing with this for 3-weeks I am asking if creating a datatype class structure in a module is possible, or is there another way to make a datatype in a module that may work?

I’ll be back at my development computer at the end of the day, so there is no rush in responding. I am using Windows 11, Xojo 2022 r4.1, Visual Studio 2022 (64-bit).

So you made a REALstructure and declared it as part of a class?
Then you may have the Class Prefix before it.

You may want to try to declare it as part of a module and put in the flags value the REALScopePublic constant.

Not quite. I made a REALstructure and declared it as part of a module. I was not able to declare it as part of a class within a module.

Unfortunately, I was unable to create a class in a module - I just couldn’t figure out the code to make a Hello World class in a module. :slight_smile:

Something like this…

static REALclassDefinition* EinhugurJSONClasses[] =
{
    &DocumentClass,
    &PrimitiveClass,
    &ArrayIteratorClass,
    &ObjectIteratorClass,
    &ObjectEntryClass,
    &JSONArrayClass,
	&ParseErrorClass,
	&SchemaDocumentClass,
	&SchemaValidatorClass
};

REALmoduleDefinition EinhugurJSONModule = {
	kCurrentREALControlVersion,
	"EinhugurJSONIII",
	0,
	0,
	nil,							// constants
	0,								// number of constants in the list
	nil,							// properties
	0,								// property count
	nil,							// structures
	0,								// structure count
	nil,							// enums
	0,							    // enum count
	nil,                            // Attributes
	0,                               // attribute count
	nil,                            // delegates 2013r1 (Control version 11 or later)
	0,                              // delegate count
	EinhugurJSONClasses,
	9
};

And inside the class definition you just name the class like “MyClass” not “MyModule.MyClass”.

If you need to inherit your own class then you put as parent “MyModule.MyClass” (full namespace)

In method and property signatures inside the class…It should work to do x as MyClass, but there are gotya’s and things that have never been fixed so I would in such cases do the syntax like: x as MyModule.MyClass

Thanks for the code Bjorn, and I am still missing something.

{
    &DocumentClass,
};

How do I define &DocumentClass? Here is how I would define it in C++, and it looks like REALclassDefinition is looking for the address of (&). The below code has &DocumentClass as an undeclared identifier.

class DocumentClass{
public:
	int MyNumber;
	void MyMethod() {
		std::cout << "test" << std::endl;
	}
};

Thanks for answering my question :slight_smile:

DocumentClass must be a REALclassDefinition to declare the document class.
Then you can reference it as pointer in the class array.

I thought somehow you had already made some Xojo class in your ventures and were just lacking to know how to put the class in a module ?

Here is class example (though you can also find plenty of them in the Open source plugins I got, even if Bob’s plugins have only been tiny bit cleaned up then they still have working examples of making class).

The class bellow shows you class that has Methods, properties, constants and enums. And the class implements 2 interfaces.

(Its usually the norm of course to use some sizeof mechanic to count the entities, but I tend to not do it in some plugins because of some pitfalls with it I have had over the years)

static REALmethodDefinition BMPExporterMethods[8] =
{
	{ (REALproc) BMPExporter_SaveToFile,   REALnoImplementation, "SaveToFile(image as RawBitmap,f as FolderItem)",REALconsoleSafe},
	{ (REALproc) BMPExporter_SaveToString, REALnoImplementation, "SaveToString(image as RawBitmap) as String",REALconsoleSafe},
    { (REALproc) BMPExporter_GetLastErrorCode, REALnoImplementation, "GetLastErrorCode() as Integer",REALconsoleSafe},
    { (REALproc) BMP_GetInternetMediaTypes, REALnoImplementation, "InternetMediaTypes() as String()", REALconsoleSafe},
    { (REALproc) BMP_GetFileExtensions, REALnoImplementation, "FileExtensions() as String()", REALconsoleSafe},
    { (REALproc) BMP_GetUTI, REALnoImplementation, "UTI() as String", REALconsoleSafe},
    { (REALproc) BMP_GetUTIDescription, REALnoImplementation, "UTIDescription() as String", REALconsoleSafe},
    { (REALproc) BMP_UTIConformsTo, REALnoImplementation, "UTIConformsTo() as String", REALconsoleSafe},
};

static REALproperty BMPExporterProperties[4] =
{
    {nil,"ErrorMessage", "String",   REALconsoleSafe, REALstandardGetter,REALnoImplementation, FieldOffset(BMPImporterData, ErrorMessage)},
    {nil,"LastError", "RawBMPExporter.ErrorValues",   REALconsoleSafe,  (REALproc)BMPExporter_LastError,REALnoImplementation, nil},
    {nil,"ProgressHandler","RawBitmap.ProgressDelegate",REALconsoleSafe,  (REALproc)BMPExporter_ProgressHandlerGetter,(REALproc)BMPExporter_ProgressHandlerSetter},
    {nil,"Aborted",        "Boolean",   REALconsoleSafe, REALstandardGetter,REALnoImplementation, FieldOffset(BMPExporterData, Aborted)},
    
};

static REALconstant BMPExporterClassConstants[1] =
{
    { PLUGIN_VERSION, nil, REALconsoleSafe | REALScopePublic, 0 , nil}
};

static const char* BMPExporter_ErrorValues[8] =
{
    "NO_ERROR = 0",
    "INVALID_PARAMETER = -2",
    "UNSUPPORTED_FORMAT = -9",
    "COULD_NOT_CREATE_FILE = -10",
    "INVALID_COLOR_SPACE = -20",
    "COULD_NOT_WRITE = -30",
    "UNKNOWN_RAWBITMAP_FORMAT = -55",
    "COULD_NOT_CONVERT_RAWBITMAP_FORMAT = -106"
};

static REALenum BMPExporterEnums[1] =
{
    {"ErrorValues","Integer",0, BMPExporter_ErrorValues,8},
};


REALclassDefinition BMPExporterClass = 
{
	kCurrentREALControlVersion, 		// Version
	"RawBMPExporter",  					// Name
	nil,								// Super Name
	sizeof(BMPExporterData),    		// Data size
	0,									// Something for system use
	(REALproc) BMPExporter_Constructor,	// Constructor index
	(REALproc) BMPExporter_Destructor,	// Destructor Index
	BMPExporterProperties,				// Pointer to property def
	4,  								// Property count
	BMPExporterMethods,					// Pointer to method def
	8,									// Method count
	nil,								// Pointer to Event def
	0,									// event Count
    nil,
    0,
    "IRawBitmapWriter, IRawBitmapWriterV2",
    nil,
    0,
    BMPExporterClassConstants,
    1,
    0,                              // Flags
    nil,
    0,
    nil,
    0,
    nil,                           
    0,
    BMPExporterEnums,                    
    1
};

And the struct that stores the actual data of the class:

struct BMPExporterData
{
    REALstring ErrorMessage;
    
	RBInteger LastError;
    
    REALobject  ProgressHandler;
    RBBoolean Aborted;
};

If you are wrapping C++ class then you could store pointer to your C++ class in this struct. And create instance of it in the Xojo constructor and delete it in the Xojo destructor.

Example of my destructor in this class:

static void BMPExporter_Destructor(REALobject instance)
{
    ClassData(BMPExporterClass, instance, BMPExporterData, me);
    
    if(me->ErrorMessage) REALUnlockString(me->ErrorMessage);
    
    REALUnlockObject(me->ProgressHandler);
}

I clean up there object reference I had to progress instance…but you would then probably just delete your C++ object. (Or deref it if it is object with some sort of reference counting mechanism)

Thanks for the hints, tips, and tricks @Christian_Schmitz and @Björn_Eiríksson.

I did make a Xojo class, and since I wasn’t able to figure it out, then I erased it and started from the beginning again. :slight_smile:

I’ll take the data that both of you supplied and work on it today.

Thank you.

Well, its been a week and I can’t seem to figure it out. I will work on another part of the program and maybe come back to this later.

Thanks for your helpful answers.

Warm regards.