dimanche 14 février 2021

Performant and Elegant Dispatching on Type for Scripting Language Implementation

I am working in the implementation of a scripting system where values are passed around as void* and an enum indicating the type.

enum class ValueType
{
    Bool,
    Float,
    Int,
    // many more types...
};

I need a workflow for implementing APIs like the following. These APIs must be as performant as possible (switch statement rather than virtual call).

void Print(ValueType type, void* value);
bool Equals(ValueType type, void* value1, void* value2);
// etc...

I would settle for the kludgy solution I have right now, but it is extra cumbersome given that I have to produce specific sets of APIs for different subsystems. Any ideas would be greatly appreciated.

Current solution (lambdas and auto are not allowed in the code base I'm working in, but I'd still be interested in seeing solutions that use those features)

// Per-type implementations
template <typename T>
class SubsystemFunctions
{
public:
    void Print(void* value)
    {
        std::cout << *static_cast<T*>(value) << std::endl;
    }

    bool Equals(void* value1, void* value2)
    {
        return *static_cast<T*>(value1) == *static_cast<T*>(value2);
    }
};

// API and gory dispatching details
class SubsystemAPI
{
public:
    static void Print(ValueType type, void* value)
    {
        DispatchOnType(type, PrintDispatcher(), value);
    }

    static bool Equals(ValueType type, void* value1, void* value2)
    {
        bool result;
        DispatchOnType(type, EqualsDispatcher(), value1, value2, &result);
        return result;
    }

private:
    struct PrintDispatcher
    {
        template <typename FunctionsT>
        void operator()(FunctionsT&& functions, void* value)
        {
            functions.Print(value);
        }
    };

    struct EqualsDispatcher
    {
        template <typename FunctionsT>
        void operator()(FunctionsT&& functions, void* value1, void* value2, bool* result)
        {
            *result = functions.Equals(value1, value2);
        }
    };

private:
    template <typename DispatcherT, typename... ArgsT>
    static void DispatchOnType(ValueType type, DispatcherT&& dispatcher, ArgsT&&... args)
    {
        switch (type)
        {
        case ValueType::Bool:
            dispatcher(SubsystemFunctions<bool>(), std::forward<ArgsT>(args)...);
            break;
        case ValueType::Float:
            dispatcher(SubsystemFunctions<float>(), std::forward<ArgsT>(args)...);
            break;
        case ValueType::Int:
            dispatcher(SubsystemFunctions<int>(), std::forward<ArgsT>(args)...);
            break;
        // etc...
        }
    }
};

Aucun commentaire:

Enregistrer un commentaire