Native object interfaces

sgs_ObjInterface object_iface[1] =
{{
    "object_name",          // type name
    NULL, NULL,             // destruct, gcmark
    NULL, NULL,             // getindex, setindex
    NULL, NULL, NULL, NULL, // convert, serialize, dump, getnext
    NULL, NULL              // call, expr
}};
sgs_CreateObject( C, NULL, malloc( sizeof( mystruct ) ), object_iface );
sgs_CreateObjectIPA( C, NULL, sizeof( mystruct ), object_iface ); // preferred method

It is very important that all memory operations on in-place allocated blocks do not, at any time, operate beyond the boundaries of those blocks. Be especially wary of putting arrays at the beginning of a structure since accidentally applying negative indices to such arrays could create issues that are extremely hard to debug.

int object_destruct( SGS_CTX, sgs_VarObj* obj )
{
    free( obj->data );
    return SGS_SUCCESS;
}
int object_getindex( SGS_CTX, sgs_VarObj* obj )
{
    char* str;
    if( sgs_ParseString( C, 0, &str, NULL ) )
    {
        if( strcmp( str, "data_pointer" ) == 0 ) return sgs_PushPtr( C, obj->data );
        
        if( strcmp( str, "do_something" ) == 0 ) return sgs_PushCFunc( C, object_do_something );
        if( strcmp( str, "do_smth_else" ) == 0 ) return sgs_PushCFunc( C, object_do_smth_else );
    }
    return SGS_ENOTFND; // return that the specified key was not found in object
}
// a slightly cleaner but less hands-on, the macro-based version:
int object_getindex( SGS_ARGS_GETINDEXFUNC )
{
    SGS_BEGIN_INDEXFUNC
        SGS_CASE( "data_pointer" ) return sgs_PushPtr( C, obj->data );
        
        SGS_CASE( "do_something" ) return sgs_PushCFunc( C, object_do_something );
        SGS_CASE( "do_smth_else" ) return sgs_PushCFunc( C, object_do_smth_else );
    SGS_END_INDEXFUNC
}