0

I want to save and access at runtime the types and default values of inputs and outputs of a function.

I have some structs to hold what I need for now in FunDeclaration and FunParam and an example function foo to reflect. Live code here: https://onlinegdb.com/BySyS8f7D

Here the full source:

#include <iostream>
#include <string>
#include <unordered_map>
#include <any>

struct FunParam {
    // This will be a custom identifier (string for now) "int", "string", etc...
    std::string type;
    // Default value for that parameter
    std::any default_value;
};

struct FunDeclaration {
    // Function input params 
    std::unordered_map<std::string, FunParam> ins;
    // Function output params
    std::unordered_map<std::string, FunParam> outs;
};

using FunctionsMap = std::unordered_map<std::string, FunDeclaration>;

void print(FunctionsMap &fmap) {
    auto printParam = [](FunParam& p) {
        if (p.type == "int") {
            std::cout << "type: " << p.type << " default_value: " << std::any_cast<int>(p.default_value);
        } else if (p.type == "double") {
            std::cout << "type: " << p.type << " default_value: " << std::any_cast<double>(p.default_value);
        } else if (p.type == "float") {
            std::cout << "type: " << p.type << " default_value: " << std::any_cast<float>(p.default_value);
        } else if (p.type == "std::string") {
            std::cout << "type: " << p.type << " default_value: " << std::any_cast<std::string>(p.default_value);
        }
    };

    for (auto& f : fmap) {
        std::cout << "Fun: " << f.first << std::endl;
        for (auto& in: f.second.ins) {
            std::cout << "\t[in] name: " << in.first << " ";
            printParam(in.second);
            std::cout << std::endl;
        }

        
        for (auto& in : f.second.outs) {
            std::cout << "\t[out] name: " << in.first << " ";
            printParam(in.second);
            std::cout << std::endl;
        }
        
    }
}



// Just an example function to work with, multiple inputs (default values), and multiple outputs
std::tuple<double, float> foo(int a = 10, std::string b = "HelloWorld") {
    return { a * 10.0, b.size() };
}

int main() {
    FunctionsMap gFuns;
    gFuns["foo"].ins = 
        { 
            {"a", {"int", std::any(int(10))} },
            {"b", {"std::string", std::any(std::string("HelloWorld"))} }
        };

    gFuns["foo"].outs = {
        {"out0", {"double", std::any(double())} },
        {"out1", {"float", std::any(float())} }
    };
    
    print(gFuns);
    return 1;
}

How would you define a macro that spits this glue code(suppose gFuns is a global), and with multiple inputs/outputs? Is this possible with just one macro? Or I have to make a macro to each possibility of in/out param numbers?

    gFuns["foo"].ins = 
        { 
            {"a", {"int", std::any(int(10))} },
            {"b", {"std::string", std::any(std::string("HelloWorld"))} }
        };

    gFuns["foo"].outs = {
        {"out0", {"double", std::any(double())} },
        {"out1", {"float", std::any(float())} }
    };

I was thinking about something like this:

#define ADD_FUN_DECL(name, in_types, in_defaultvalues, out_types, out_defaultvalues)

FrameBuffer
  • 757
  • 1
  • 7
  • 26

1 Answers1

0

I was thinking about something like this:

I would group default value close with the type. I noticed that both paths - for input and outputs values - are similar, so I merged them together, as they only seem to differ with default value.

The following code could be enough to get you started. For more arguments, make sure to research how to overload macro on number of arguments. Note how HANDLE_PARAMS instuff is expanding the macro because instuff contains (....) brackets. The same happens in HANDLE_GENERIC. This is a trick for creating "lists of lists" in preprocessor.

#define HANDLE_GENERIC(type, name, ...)  \
        { #name, { #type, std::any(type(__VA_ARGS__)) } }

// Overloading the macro on more number of arguments left to the reader.
#define HANDLE_PARAMS_2(a, b)  HANDLE_GENERIC a , HANDLE_GENERIC b
#define HANDLE_PARAMS_N(_2,_1,N,...)  HANDLE_PARAMS##N
#define HANDLE_PARAMS(...)  HANDLE_PARAMS_N(__VA_ARGS__,_2,_1)(__VA_ARGS__)

#define ADD_FUN_DECL(name, instuff, outstuff) \
    gFuns[#name].ins = {  \
        HANDLE_PARAMS instuff \
    }; \
    gFuns[#name].outs = { \
        HANDLE_PARAMS outstuff \
    }; \

ADD_FUN_DECL(
   // the name of the struct
   foo,
   // a () list of (type, variablename, defaultvalue) for input values
   (
      (int, a, 10),
      (std::string, b, "HelloWorld")
   ),
   // a () list of (type, variablename) for output values
   (
      (double, out0),
      (float, out1)
   )
)

The code above results in the following:

gFuns["foo"].ins = { { "a", { "int", std::any(int(10)) } } , { "b", { "std::string", std::any(std::string("HelloWorld")) } } }; gFuns["foo"].outs = { { "out0", { "double", std::any(double()) } } , { "out1", { "float", std::any(float()) } } };
KamilCuk
  • 120,984
  • 8
  • 59
  • 111