subpixel: Why is this nasty pointer recasting code still all up in my face? (FFGL.cpp)
Menno: Plugins aren't expected to interact with FFGL.cpp. Plugins are expected to use the FFGLPluginSDK.h as their entrypoint into the ffgl sdk. FFGL's internals require all this pointer casting because ffgl uses an opcode based interface. Everything passed between the plugin and it's host needs to be a generic type, pointers are used because then you can have the specification dictate what is pointed to, which is usually just a structure wrapping the actual arguments/return values.
subpixel: I come from a C background and understand what the pointer recasting is for. What I was asking is: why not hide all that ugly mess and encourage stronger typing within the SDK for types other than unsigned int?
I don't know if you had a chance to peruse the code I posted - I made a bunch of changes to make nearly all the (type *) casts go away, and to not have method/function arguments and return values throwing (void*) pointers around.
Change to FFMixed in FFGL.h, from:
Code: Select all
typedef union FFMixed
{
FFUInt32 UIntValue;
void* PointerValue;
} FFMixed;
to:
Code: Select all
// Forward declaration of types referenced in union FFMixed
class CFreeFrameGLPlugin;
typedef struct PluginInfoStructTag PluginInfoStruct;
typedef struct PluginExtendedInfoStructTag PluginExtendedInfoStruct;
typedef struct SetParameterStructTag SetParameterStruct;
typedef struct SetBeatinfoStructTag SetBeatinfoStruct;
typedef struct SetHostinfoStructTag SetHostinfoStruct;
typedef struct RangeStructTag RangeStruct;
typedef struct GetRangeStructTag GetRangeStruct;
typedef struct FFGLViewportStructTag FFGLViewportStruct;
typedef struct FFGLTextureStructTag FFGLTextureStruct;
typedef struct ProcessOpenGLStructTag ProcessOpenGLStruct;
typedef struct GetParameterElementNameStructTag GetParameterElementNameStruct;
typedef struct GetParameterElementValueStructTag GetParameterElementValueStruct;
typedef struct SetParameterElementValueStructTag SetParameterElementValueStruct;
typedef union FFMixed
{
FFUInt32 UIntValue;
void* PointerValue;
float FloatValue;
double* pDouble;
const char* StrValue;
CFreeFrameGLPlugin* pCFreeFrameGLPlugin;
const PluginInfoStruct* pPluinInfo;
const PluginExtendedInfoStruct* pPluginExtendedInfo;
SetParameterStruct* pSetParameter;
SetBeatinfoStruct* pSetBeatinfo;
SetHostinfoStruct* pSetHostinfo;
RangeStruct* pRange;
GetRangeStruct* pGetRange;
FFGLViewportStruct* pFFGLViewport;
FFGLTextureStruct* pFFGLTexture;
ProcessOpenGLStruct* pProcessOpenGL;
GetParameterElementNameStruct* pGetParameterElementName;
GetParameterElementValueStruct* pGetParameterElementValue;
SetParameterElementValueStruct* pSetParameterElementValue;
} FFMixed;
There is no harm to the interface; everything is still a 32-bit value, whether that represent an unsigned int, float, pointer to basic type (double*) or pointer to structure/class object. Ideally, more (if not all) of union members pointers could be const pointers, and this definition used in both host and plugin code to encourage proper respect of data other's data structures.
Example 1.
Code: Select all
case FF_GETINFO:
retval.PointerValue = (PluginInfoStruct*)getInfo();
break;
In this case the cast is redundant since Pointervalue is (void*) and getInfo() has already anonymised and stripped any constness:
Code: Select all
void* getInfo()
{
return (void*)( g_CurrPluginInfo->GetPluginInfo() );
}
My version:
Code: Select all
// 2019-07-11 ffgl@subpixels.com - changed return type from (void*) to (const PluginInfoStruct*)
const PluginInfoStruct* getInfo()
{
return g_CurrPluginInfo->GetPluginInfo();
}
//...
switch( functionCode )
{
case FF_GETINFO:
retval.pPluinInfo = getInfo();
break;
Example 2.
Code: Select all
switch( functionCode )
//...
case FF_SETPARAMETER:
if( pPlugObj != NULL )
{
if( getParameterType( ( (const SetParameterStruct*)inputValue.PointerValue )->ParameterNumber ) == FF_TYPE_TEXT )
{
retval.UIntValue = pPlugObj->SetTextParameter( ( (const SetParameterStruct*)inputValue.PointerValue )->ParameterNumber,
(const char*)( (const SetParameterStruct*)inputValue.PointerValue )->NewParameterValue.PointerValue );
}
else
{
retval.UIntValue = pPlugObj->SetFloatParameter( ( (const SetParameterStruct*)inputValue.PointerValue )->ParameterNumber,
( *(float*)&( (const SetParameterStruct*)inputValue.PointerValue )->NewParameterValue.UIntValue ) );
}
}
else
{
retval.UIntValue = FF_FAIL;
}
break;
case FF_GETPARAMETER:
if( pPlugObj != NULL )
{
if( getParameterType( inputValue.UIntValue ) == FF_TYPE_TEXT )
{
retval.PointerValue = pPlugObj->GetTextParameter( inputValue.UIntValue );
}
else
{
float fValue = pPlugObj->GetFloatParameter( inputValue.UIntValue );
retval.UIntValue = *(FFUInt32*)&fValue;
}
}
else
{
retval.UIntValue = FF_FAIL;
}
break;
//...
}
becomes
Code: Select all
FFMixed retval;
retval.UIntValue = FF_FAIL;// Assume failure until we succeed
//...
switch( functionCode )
{
//...
case FF_SETPARAMETER:
if( pPlugObj != NULL )
{
const auto args = inputValue.pSetParameter;
if( getParameterType( args->ParameterNumber ) == FF_TYPE_TEXT )
retval.UIntValue = pPlugObj->SetTextParameter( args->ParameterNumber, args->NewParameterValue.StrValue );
else
retval.UIntValue = pPlugObj->SetFloatParameter( args->ParameterNumber, args->NewParameterValue.FloatValue );
}
break;
case FF_GETPARAMETER:
if( pPlugObj != NULL )
{
if( getParameterType( inputValue.UIntValue ) == FF_TYPE_TEXT )
retval.StrValue = pPlugObj->GetTextParameter( inputValue.UIntValue );
else
retval.FloatValue = pPlugObj->GetFloatParameter( inputValue.UIntValue );
}
break;
//...
}
Example 3.
Code: Select all
case FF_SET_BEATINFO:
if( pPlugObj != NULL )
{
const SetBeatinfoStruct* beatInfo = reinterpret_cast< const SetBeatinfoStruct* >( inputValue.PointerValue );
float bpm = *(float*)&beatInfo->bpm.UIntValue;
float barPhase = *(float*)&beatInfo->barPhase.UIntValue;
pPlugObj->SetBeatInfo( bpm, barPhase );
retval.UIntValue = FF_SUCCESS;
}
else
{
retval.UIntValue = FF_FAIL;
}
break;
becomes
Code: Select all
case FF_SET_BEATINFO:
if( pPlugObj != NULL )
{
const auto args = inputValue.pSetBeatinfo;
pPlugObj->SetBeatInfo( args->bpm, args->barPhase );
retval.UIntValue = FF_SUCCESS;
}
break;
Yes, we are still making an assumption about what the 32-bit value is coming in (based on the conventions of the interface with the FFGL host), but within the SDK we can use types for accuracy and clarity.