Friday 27 November 2020

Changing the name of a variable in CMake

 Here's a neat trick in CMake: you want to change the name of a variable, but worry that anyone you've distributed the code to already will lose the option they've selected.

Use the old variable as the default value for the new one:

option(OLD_VARIABLE "Some variable" ON)
option(NEW_VARIABLE "Some variable" ${OLD_VARIABLE})

or...

set( OLD_STRING_VARIABLE "Old default" CACHE STRING "Help text" )
set( NEW_STRING_VARIABLE "${OLD_STRING_VARIABLE}" "Help text" )

Thursday 12 November 2020

Passing an array of structs from C++ to C#

To pass an array of structs from C++ to C#, you can pass a pointer to a C-style array. In C++ you may have a struct, e.g.


#pragma pack(push)
#pragma pack(1)
struct InputEvent
{
uint32_t eventId;
float floatValue;
uint32_t intValue;
};
#pragma pack(pop)

The delegate in C++ is:
typedef void(__stdcall* ProcessNewInputFn) (int numEvents, const InputEvent**); Telling C++ what C# function to call: 

 
extern "C" __declspec(dllexport) void SetInputProcessingDelegate(ProcessNewInputFn newInputProcessing)
{
processNewInput = newInputProcessing;
}

Using this from C++

std::vector<InputEvent> inputEvents;
const avs::InputEvent *v=inputEvents.data(); processNewInput(inputEvents.size(), &v); 

In C# the struct is defined as:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct InputEvent
{
public UInt32 eventId;
public float floatValue;
public UInt32 intValue;
}; 



Note the packing! It must match what we had in C++. Now C# must declare the the delegate type it will implement:

[UnmanagedFunctionPointer(CallingConvention.StdCall)] delegate void OnNewInput(int numEvents, in IntPtr newEvents); 


And declare in C# the C++ function that sets the delegate:

[DllImport("dllname")] static extern void SetInputProcessingDelegate(OnNewInput onNewInput );


This is called with

ok = SetInputProcessingDelegate(ProcessingClass.SetInput); 


Where we have a class like this:


class ProcessingClass
{
public static void StaticProcessInput(int numEvents, in IntPtr inputEventsPtr )
{
    int EventSize = System.Runtime.InteropServices.Marshal.SizeOf(typeof(avs.InputEvent));
    avs.InputEvent[] inputEvents = new avs.InputEvent[inputState.numEvents];
    IntPtr ptr=  inputEventsPtr;
    for (int i = 0; i < inputState.numEvents; i++)
    {
       inputEvents[i]=Marshal.PtrToStructure<avs.InputEvent>(ptr);
       ptr += EventSize;
}
        }
}


Here, we take the C++ style pointer-to-array, and iterate through the array elements, copying each in turn into the C# style array.