1

I am working on a custom FFI framework for a language and am running in to the issue of needing to instantiate templates in every possible branch of a comparison. When this becomes recursive the number of instantiations (and compile time) explodes. The implementation looks like this:

template<template<typename...> class F, typename ... Args>
struct TypeApp{
    template<typename ... UArgs>
    static auto apply(Type t, UArgs &&... uargs){
        // all of the templates in these branches will be instantiated
        if(t == type_<void>){ return F<Args..., void>::apply(std::forward<UArgs>(uargs)...); }
        else if(t == type_<bool>){ return F<Args..., bool>::apply(std::forward<UArgs>(uargs)...); }
        else if(t == type_<int>){ return F<Args..., int>::apply(std::forward<UArgs>(uargs)...); }
        // ...
        else{ assert(!"unrepresentable type"); }
    }
};

template<typename...> struct FFICaller;

template<std::size_t ParamsRem, typename Ret, typename ... Params>
struct FFICaller<std::integral_constant<std::size_t, ParamsRem>, Ret, Params...>{
    static auto apply(std::span<Type> params){
        return TypeApp<FFICaller, std::integral_constant<std::size_t, ParamsRem-1>, Ret, Params...>
                ::apply(params[0], params.subspan(1));
    }
};

template<typename Ret, typename ... Params>
struct FFICaller<std::integral_constant<std::size_t, 0>, Ret, Params...>{
    static FFIFn apply(){
        return +[](void *ptr, const std::vector<Value> &args){
            // call 'ptr' with 'args'
        };
    }
};

So when I try to do something like this:

void doFFIStuff(void *ptr, Type result, const std::vector<Type> &params){
    FFIFn fn;
    switch(params.size()){
        case 1: fn = TypeApp<FFICaller, std::integral_constant<std::size_t, 1>>::apply(result, params); break;
        case 2: fn = TypeApp<FFICaller, std::integral_constant<std::size_t, 2>>::apply(result, params); break;
        case 3: fn = TypeApp<FFICaller, std::integral_constant<std::size_t, 3>>::apply(result, params); break;
        case 4: fn = TypeApp<FFICaller, std::integral_constant<std::size_t, 4>>::apply(result, params); break;
        default: assert(!"only up to 4 arguments currently supported");
    };
}

My compiler freezes in shear terror at the mountain of code it has to process.

Is there some way I can restructure this code so that it isn't instantiating every branch multiple times?

EDIT:

Here is a minimal example on godbolt where if you comment out case 4 and 5 from the switch in getFFIFn it will compile.

RamblingMad
  • 5,332
  • 2
  • 24
  • 48
  • Did you mean `return TypeApp, /*...*/` ? – Piotr Skotnicki Jul 11 '20 at 07:32
  • And it does not solve the problem ? – Piotr Skotnicki Jul 11 '20 at 08:30
  • @PiotrSkotnicki no, just a mistake copying the code over – RamblingMad Jul 11 '20 at 08:31
  • Please make a proper [MCVE](https://stackoverflow.com/help/minimal-reproducible-example) (e.g. on coliru, godbolt, or wandbox) and link to it in your question. Otherwise we can only guess as to whether our suggestions might help you (as @Piotr did). – ildjarn Jul 11 '20 at 08:32
  • Side note: You could probably replace that enum with a variant of proxy types. Might cut away a good bit of the code. That means doing things like as in https://stackoverflow.com/a/62780979/5684257 and https://stackoverflow.com/a/61310826/5684257 – HTNW Jul 11 '20 at 15:37
  • Are the functions you use (i.e. std::fmaf) known at compilation time? – Benny K Jul 21 '20 at 08:44
  • @BennyK in what context? – RamblingMad Jul 21 '20 at 08:48
  • You chose `std::vector` for `paramTypes`, and you rely heavily on `if` and `switch` statements. Can these possibly be replaced with `std::array`? Do you always know the number of arguments and their types during compilation? – Benny K Jul 21 '20 at 08:54
  • @BennyK The FFI is for an interpreter which can call arbitrary C functions, so it can't always be known. As a backup I currently just use libffi. – RamblingMad Jul 21 '20 at 08:58

0 Answers0