FormalSignature represents the arguments for a function in an interpreted language. You can imagine we're writing the interpreter in C++, and want to implement a few built-in functions in C++ rather than in the interpreted language. To make that work safely, FormalSignature gives us a mapping from types in the interpreted language to types in C++.
Each parameter to the built-in is represented by a ParameterSpec, templated on the C++ type and containing some fields to represent the parameter names and the type in the embedded language.
A FormalSignature for Euclid's Greatest Common Divisor algorithm might look like this:
const FormalSignature<
ParameterSpec<unsigned int>,
ParameterSpec<unsigned int>
> Gcd{
"GCD", "Compute the greatest common divisor of the first two arguments. Place the result in the third",
ParameterSpec<unsigned int>{
"a", // name of the first arg
"positive integer", // typeId for the embedded language
InputOrOutput::Input
},
ParameterSpec<unsigned int>{
"b", "positive integer", InputOrOutput::Input
},
ParameterSpec<unsigned int>{
"d", "positive integer", InputOrOutput::Output
}
};In order to invoke one of these built-ins, the FormalSignature must be bound to actual values that exist within the runtime language. We call this operation connect and pass in a vector<string> of the names (of variables in the interpreted language) that the function is being called with. This produces an ActualSignature. (We're using "formal" and "actual" parameters in the programming language sense. The formals are the names internal to the built-in. The actuals are the values on which the function is invoked.)
(Solved this using https://github.com/BlackMATov/invoke.hpp)
We have a nice solution (thanks to Scott) for converting ParameterSpec<T>s into ActualParameter<T>s. It produces a std::tuple<ActualParameter<T>, ActualParameter<U>, ...> as we need. But it accepts a variable number of ParameterSpec<T> arguments. In a FormalSignature, I have them stored in a tuple.
How can I apply this tuple of arguments into the connect function. A minimal example removed from context is in variadic.cpp.
I'm okay with explicitly listing out the template parameters when instantiating a FormalSignature. However, I don't want to have to repeat that information when I create an ActualSignature using FormalSignature<Ts...>::connect, nor when I write a class that will own an ActualSignature. The signatures in the actual code frequently have 10-15 parameters, and my goal with this refactor is to avoid duplicating the knowledge of their names and order.
How can I expose a template alias, FormalSignature<Ts...>::ActualSignatureT that has all the template parameters filled in? For example:
using Example = FormalSignature<
ParameterSpec<unsigned int>,
ParameterSpec<string>,
ParameterSpec<double>
>;
static_assert(std::is_same<
Example::ActualSignatureT,
ActualSignature<
ActualParameter<unsigned int>,
ActualParameter<string>,
ActualParameter<double>
>
>::value);I feel like I'm close with this, but can't quite make it work:
// AsParam converts from a ParameterSpec<T> to an ActualParameter<T> (this part works)
template<typename T>
struct AsActualParam {
using Param = T;
};
template <template <typename> class PSpec, typename T>
struct AsActualParam<PSpec<T>> {
static_assert(std::is_same<PSpec<T>, ParameterSpec<T>>::value, "AsActualParam can only be used with a ParameterSpec<T>");
using Param = ActualParameter<T>;
};
// succeeds
static_assert(std::is_same<
AsActualParam<ParameterSpec<double>>::Param,
ActualParameter<double>
>::value);Then, we need to do the same thing for a parameter pack of ParamSpec...
template <
class PSpec, // does this need to be template<typename> class PSpec?
typename... Rest>
struct AsActualSignature {
using ActualSignatureT = ActualSignature<
// Pretty sure the beginning is right.
AsParam<PSpec>::Param,
// This line feels wrong, ActualSignatureT is a concrete type, not a parameter pack
AsActualSignature<Rest...>::ActualSignatureT...
>;
};
// also, feels like I need a base case for AsActualSignature?
Another potential solution
```cpp
template <typename... PSpecs>
struct AsActualSignature {
using ActualSignatureT = ActualSignature<
(AsParam<PSpecs>::Param)...
// I want the line above to expand PSpecs to produce
// AsParam<PSpecs0>::Param, AsParam<PSpecs1>::Param, AsParam<PSpecs2>::Param, ...
// but it doesn't looks like that's a supported type of expansion
>;
};
static_assert(std::is_same<
ActualSignature<ActualParameter<double>, ActualParameter<int>>,
AsActualSignature<ParameterSpec<double>, ParameterSpec<int>>::ActualSignatureT
>::value);// This is the goal, but it currently fails. static_assert(std::is_same< AsActualSignature< ParameterSpec, ParameterSpec, ParameterSpec
::ActualSignatureT, ActualSignature< ActualParameter, ActualParameter, ActualParameter
::value);
I've been playing with this problem in [main.cpp](#file-main-cpp)