How to use type-level functions to create static types, dynamically? - c++

In TypeScript, there are type-level functions that allow creating new types based on given literal types/specifications (see Mapped Types, Conditional Types, etc.).
For instance, here is such a function, let say provided by a lib author:
type FromSpec<S> = {
[K in keyof S]: S[K] extends "foo" ? ExampleType : never
};
Its purpose is, given a specification S in the form of a map of string keys and arbitrary literals, it creates a new type in the form of a map with the same set of keys and with values transformed. If a the value is the literal "foo" then it becomes the type ExampleType, otherwise the value is rejected by transforming it into the bottom type never.
Then, an end-user can make use of this function to create new types following the above explanation:
type Example = FromSpec<{some_key: "foo", another_key: "bar"}>
// = {some_key: ExampleType, another_key: never}
It's noteworthy that the lib author doesn't know about what exact type a given end-user may want, and thus provides him with a function to create the ones he needs. On the other hand, the end-user can create an infinite set of new types as long as he complies with the function's capabilities.
You can play around this simple example, here.
The question is about how this kind of "dynamism" is expressible in other typed languages (e.g., ReasonML/OCaml, Scala, Haskell). Or how, as an end-user, to create new types, at compile-time, by using type-level functions, provided by a lib author (as one would usually do at runtime with value-level functions)?
It's important to note that the question is not about which language is better, etc. It's about finding the most straightforward and explicit way to express such capabilities. Here we saw an example in TypeScript, but is there any more natural way in any other language?

Given Scala is one of the tagged languages, here is a solution in Dotty (aka. Scala 3). Take this with a grain of salt, since Dotty is still under development. Tested with Dotty version 0.24.0-RC1, here is a Scastie that proves this actually compiles.
Scala doesn't have the same sort of built-in type machinery as TypeScript for manipulating records. Not to fear, we can roll our own!
import deriving._
// A field is literally just a tuple of field name and value
type Field[K, V] = (K, V)
// This just helps type-inference infer singleton types in the right places
def field[K <: String with Singleton, V <: Singleton](
label: K,
value: V
): Field[K, V] = label -> value
// Here is an example of some records
val myRec1 = ()
val myRec2 = field("key1", "foo") *: field("key2", "foo") *: ()
val myRec3 =
field("key1", 1) *: field("key2", "foo") *: field("key3", "hello world") *: ()
Then, FromSpec can be implemented using a match-type. The never type in TypeScript is called Nothing in Scala/Dotty.
// Could be defined to be useful - `trait` is just an easy way to bring a new type in
trait ExampleType
val exampleValue = new ExampleType {}
type FromSpec[S <: Tuple] <: Tuple = S match {
case Field[k, "foo"] *: rest => Field[k, ExampleType] *: FromSpec[rest]
case Field[k, v] *: rest => Field[k, Nothing] *: FromSpec[rest]
case Unit => Unit
}
Finally, let's use FromSpec:
def myRec1Spec: FromSpec[myRec1.type] = ()
def myRec2Spec: FromSpec[myRec2.type] =
field("key1", exampleValue) *: field("key2", exampleValue) *: ()
def myRec3Spec: FromSpec[myRec3.type] = ??? // no non-diverging implementation

Is it possible to express the same kind of "dynamism" or something close to it in another typed language (e.g., ReasonML/OCaml, Scala, Haskell).
Yes, dynamic types are fully supported by the OCaml/ReasonML type system and are widely used. You can express quite complex dynamic typing rules, e.g., build your hierarchies, implement ad-hoc polymorphism and so on. The main ingredients of the solution is using extensible GADT, first-class modules, and existentials. See this answer as one of the example or this discussion for the general case of universal values, there are also multiple libraries that provide various dynamic typing capabilities in OCaml. Another example is BAP's Core Theory library that has a very complex type hierarchy for value sorts, which includes precise type specifications for various number representations, including floating-point numbers, memories, etc.
To make the answer complete, this is how you can implement your fromSpec in OCaml, first we define type that will be bearing the tag for dynamic typing, underneath the hood this is just an integer, but with associated type which it is witnessing,
type 'a witness = ..
To create a new witness (basically incrementing this id) we will use first class modules and append a new constructor using +=
module type Witness = sig
type t
type _ witness += Id : t witness
end
type 'a typeid = (module Witness with type t = 'a)
let newtype (type u) () =
let module Witness = struct
type t = u
type _ witness += Id : t witness
end in
(module Witness : Witness with type t = u)
The type equality proof (the value that proofs to the compiler that two types are the same since they are both using the constructor with the same identity), is commonly represented as ('a,'b) eq type,
type ('a,'b) eq = Equal : ('a,'a) eq
And this is how we implement the cast function,
let try_cast : type a b. a typeid -> b typeid -> (a,b) eq option =
fun x y ->
let module X : Witness with type t = a = (val x) in
let module Y : Witness with type t = b = (val y) in
match X.Id with
| Y.Id -> Some Equal
| _ -> None
finally, your fromSpec,
type spec {
data : 'a;
rtti : 'a typeid
}
let example_type = newtype ()
let example = {
data = 42;
rtti = example_type; (* witnesses that data is `int` *)
}
let fromSpec = try_cast example_type

Disclaimer: I'm not a C++ programmer, so don't take this answer to be the proper way to do it in C++. It is just one way to do it that is extremely brittle and is probably mostly wrong.
//I've used char pointers below, because it's not possible to directly write string //literals in templates without doing some more complex stuff that isn't relevant here
//field1 and field2 are the names of the fields/keys
const char field2[] = "field2";
const char field1[] = "field1";
//foo and bar are the strings that determine what the
//type of the fields will be
const char foo[] = "foo";
const char bar[] = "bar";
//This represents a key and the determining string (foo/bar)
template <const char * name, const char * det>
struct Named {};
//What the type of the field will be if it maps to "foo"
struct ExampleType {
std::string msg;
};
//The end of a cons structure
struct End{};
//A cons-like structure, but for types
template <typename T, typename N>
struct Cons {
typedef T type;
typedef N Next;
};
//This'll be used to create new types
//While it doesn't return a type, per se, you can access the
//"created" type using "FromSpec<...>::type" (see below)
template <typename T>
struct FromSpec;
//This will handle any Named template where the determining string
//is not "foo", and gives void instead of ExampleType
template <const char * name, const char * det, typename rest>
struct FromSpec<Cons<Named<name, det>, rest>> {
//Kinda uses recursion to find the type for the rest
typedef Cons<void, typename FromSpec<rest>::type> type;
};
//This will handle cases when the string is "foo"
//The first type in the cons is ExampleType, along with the name
//of the field
template <const char * name, typename rest>
struct FromSpec<Cons<Named<name, foo>, rest>> {
typedef Cons<ExampleType, typename FromSpec<rest>::type> type;
};
//This deals with when you're at the end
template <>
struct FromSpec<End> {
typedef End type;
};
Now you can use it like this:
typedef Cons<Named<field1, foo>, Cons<Named<field2, bar>, End>> C;
//Notice the "::type"
typedef FromSpec<C>::type T;
T is equivalent to Cons<ExampleType, Cons<void, End>>
You can then access the types inside like so:
typedef T::type E; //Equivalent to ExampleType
typedef T::type::Next N; //Equivalent to Cons<void, End>
typedef N::type v; //Equivalent to void
Example usage
int main() {
ExampleType et = { "This is way too complicated!" };
//You can kinda have values of type "void", unfortunately,
//but they're really just null
// v
N inner = { nullptr, new End() };
T obj = { &et, &inner };
Cons<ExampleType, Cons<void, End>> obj2 = obj;
std::cout << et.msg << std::endl;
}
Prints "This is way too complicated!"
Link to repl.it
Feel free to edit my answer if it has mistakes or if it could be otherwise improved. I mostly just tried to translate the answer by #Alec into C++.

Related

Is there a way to make a type variable in c++?

I am curious about a way to make a type variable.
What I mean by that is explained in the code below:
using var = type_var<int>; // currently var is of type type_var<int>
/// somewhere like in constexpr functions
var::store<float>; // now var is of type type_var<float>
static_assert(std::is_same<var::get_type, float>::value, "");
Of course, as far as I know, this code will never work, since using will make var 'immutable'.
But still, I wonder if there is a way to store types mutably.
What I am asking in this question is, is there a way to make an element that stores 'type' which type contained in the element can change in compile time.
The simple answer is No!
The c++ programming language did not have something like "compile time variables". All and everything follows the One Definition Rule (ODR)
C++ offers with templates a own kind of compile time language, often named as Template MetaProgramming (TMP) The TMP language uses the general concept of a functional programming language.
Taken from the above linked text:
Functional programs do not have assignment statements, that is, the value of a variable in a functional program never changes once defined.
If I understand your pseudo example code, you think something like the following:
template < auto x >
struct Variable
{
static constexpr decltype(x) value = x;
};
Variable< 10 > intvar;
Variable< 'a' > charvar;
int main()
{
// accessing the data:
std::cout << intvar.value << std::endl;
std::cout << charvar.value << std::endl;
}
But in the world of templates and types you have no chance to "assign" a new value nor type to the template anymore, simply by not having any kind of syntax for it.
You can program also algorithms in TMP, but all the "results" of "calls" are not in any kind variable, they always define new "values".
Example of some template metaprogramming. The example shows how to write a
"add" "function". It will add two type containers...
// define a data structure which can contain a list of types
template < typename ... TYPES >
struct TypeContainer;
// let us define some TMP "variables", which are types in c++
using list1 = TypeContainer<int, float, int >;
using list2 = TypeContainer< char, bool, int >;
// and now we define a TMP "function"
template < typename ... PARMS > struct Concat;
// which simply adds two typelists
template < typename ... LIST1, typename ... LIST2 >
struct Concat< TypeContainer< LIST1... >, TypeContainer< LIST2...>>
{
using RESULT = TypeContainer< LIST1..., LIST2...>;
};
using list3 = Concat<list1, list2 >::RESULT;
// But you never can change the "variable", because of the
// One Definition Rule (ODR)
// ( will fail to compile )
//using list2 = Concat<list1, list2 >::RESULT;
// helper to let us know what we have in the typelists:
// works for gcc or clang but is implementation specific
template < typename T>
void Print()
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main()
{
Print<list3>();
}
You will find such algorithms already defined in the STL. There you have std::tuple as the type container and std::tuple_cat to do the "add" of two tuples. My code should only give you simple example to understand what we are doing without doing some magic things from inside the STL.

Derive a type A from a list of strings a

Say I have a constant and immutable list of strings a
const a = ['b', 'c', 'd']
and I want to create a type A that looks like this:
type A = 'b' | 'c' | 'd'
My question is: How do I create the type A from the list a automatically?
The other way around would work for me as well: How to create the list a given the type A
Yes, you can do this. The only barrier is that if you write
const a = ['b','c','d'];
// inferred as string[]
then a will be inferred as string[], so the literal values 'a', 'b', and 'c' are forgotten by the time you try to derive A. You can always annotate the type of a explicitly:
const a: ['b','c','d'] = ['b','c','d'];
type A = typeof a[number];
//type A = "b" | "c" | "d"
but now you are repeating yourself, which you are trying to avoid. There is a solution:
In an answer to another question I recommended a helper function (tuple() in tuple.ts) to infer tuple types.
const a = tuple('b','c','d');
//inferred as type ["b", "c", "d"]
type A = typeof a[number];
//type A = "b" | "c" | "d"
If you don't care about it being a tuple, you can write a simple array inference helper function instead:
function inferLiteralArray<T extends string>(...arr: T[]): T[] {
return arr;
}
const a = inferLiteralArray("b", "c", "d");
//inferred as type ("b" | "c" | "d")[]
type A = typeof a[number];
//type A = "b" | "c" | "d"
Either way works, and you don't need to repeat yourself. Hope that helps; good luck!
Update 1
#fritz_da_silva said:
awesome, it works neatly!!! the a[number] syntax looks like black magic to me. could u please elaborate on why it works?
Sure. The syntax should be interpreted like this (see the parentheses I added for clarity):
type A = (typeof a)[number];
The typeof a part is using the typeof type query operator to get the type of a, which (for the tuple() example) is ["b", "c", "d"].
Then I am using the indexed access operator. If you have a type T and a type K of one of its keys (so it should extend keyof T; you can use string literals like "length" or numeric literals like 0 or unions of these), then T[K] is the type of the property accessible at that key. For an arraylike type A, the type A[number] gets the type of the elements of that array type. In the tuple example, this turns out to be a union, because there are multiple keys of type number with different property types. (When you use the indexed access operator, T[K1|K2] should be equal to T[K1]|T[K2]). Observe:
type TheTuple = typeof a; // ['b','c','d'];
type Zeroth = TheTuple[0]; // 'b'
type First = TheTuple[1]; // 'c'
type Second = TheTuple[2]; // 'd'
type A = TheTuple[number]; // 'b'|'c'|'d'
Does that make sense?
What you want to do is not possible as the array is created at runtime, and changes to the array cannot be determined by the compiler. Therefore, it cannot enforce a type system.
For example, consider the following:
const array = [ "Bob", "James", "Dan" ];
let value: *A key of array*
setTimeout(() => {
value = "James";
}, 1000);
array.splice(1, 1);
What would happen in the above scenario? By the time value = "James is called, "James" has been removed from the array, so an error should be thrown! But this occurs at runtime so the compiler is unable to warn you.
The workaround is to declare a type with the following construct, however it cannot be modified:
type MyType = "Bob" | "James" | "Dan"
let value: MyType = "Dan";
value = "Steven" // <- Compiler error!
EDIT: saravana sums it up quickly in their comment.

How to construct a tuple from an array

I am designing a C++ library that reads a CSV file of reported data from some experiment and does some aggregation and outputs a pgfplots code. I want to make the library as generic and easy to use as possible. I also want to isolate it from the data types that are represented in the CSV file and leave the option to user to parse each column as she desires. I also want to avoid Boost Spirit Qi or other heavy duty parser.
The simple solution I have is for the user to create a type for each column, with a constructor that takes "char *". The constructor does its own parsing for the value it is given, which is one cell from the data. The user then passes me a list of types; the schema, representing the types in a line of data. I use this type list to create a tuple, in which every member of the tuple is responsible for parsing itself.
The problem now is how to initialise (construct) this tuple. Dealing with tuples is of course not straightforward since iterating over their elements is mostly a compile-time operation. I used Boost Fusion at first to achieve this task. However, the function I used (transform) although might take a tuple as input (with the appropriate adapter), it does not seem to return a tuple. I need the return value to be a tuple so some other code can use it as an associative type-to-value container (access it by type via std::get<T>), while using only standard tools, that is, without using Boost. So I had to convert whatever Fusion's transform returned into std::tuple.
My question is how to avoid this conversion, and better yet how to avoid Boost Fusion completely.
A simple solution that comes to mind is to use the constructor of std::tuple, and somehow pass each element its respective "const *" that it needs to construct. However, while this is possible using some complicated template-based enumeration techniques, I am wondering if there is a straightforward "parameter-pack"-like approach, or an even simpler way to pass the values to the constructors of the individual elements of a tuple.
To clarify what I am seeking, kindly take a look at this following code.
#include <cstdio>
#include <array>
template <typename...> struct format {};
template <typename...> struct file_loader {};
template <typename... Format>
struct
file_loader<format<Format...> > {
void load_file() {
size_t strsize = 500u;
char *str = new char[strsize]();
auto is = fopen("RESULT","r");
/* example of RESULT:
dataset2,0.1004,524288
dataset1,0.3253,4194304
*/
while(getline(&str, &strsize, is) >= 0) {
std::array<char*, 3> toks{};
auto s = str;
int i = 2;
while(i --> 0)
toks[i] = strsep (&s, ",");
toks[2] = strsep (&s, ",\n");
std::tuple<Format...> the_line{ /* toks */ } ; // <-- HERE
//// current solution:
// auto the_line{
// as_std_tuple( // <-- unnecessary conversion I'd like to avoid
// boost::fusion::transform(boost::fusion::zip(types, toks), boost::fusion::make_fused( CAST() ))
// )};
// do something with the_line
}
}
};
#include <string>
class double_type {
public:
double_type() {}
double_type(char const *token) { } // strtod
};
class int_type {
public:
int_type() {}
int_type(char const *token) { } // strtoul
};
int main(int argc, char *argv[]) {
file_loader< format< std::string,
double_type,
int_type > >
{}.load_file();
return 0;
}
I've highlighted the interesting line as "HERE" in a comment.
My question precisely is:
Is there a way to construct a std::tuple instance (of heterogeneous
types, each of which is implicitly convertible from "char *") with
automatic storage duration (on the stack) from a std::array<char *, N>,
where N equals the size of that tuple?
The answer I am seeking should
Avoid Boost Fusion
(Simplicity condition) Avoid using more than 5 lines of boilerplate template-based enumeration code
Alternatively, shows why this is not possible to do in the C++14 standard
The answer can use C++17 constructs, I wouldn't mind.
Thank you,
As with all questions involving std::tuple, use index_sequence to give you a parameter pack to index the array with:
template <class... Formats, size_t N, size_t... Is>
std::tuple<Formats...> as_tuple(std::array<char*, N> const& arr,
std::index_sequence<Is...>)
{
return std::make_tuple(Formats{arr[Is]}...);
}
template <class... Formats, size_t N,
class = std::enable_if_t<(N == sizeof...(Formats))>>
std::tuple<Formats...> as_tuple(std::array<char*, N> const& arr)
{
return as_tuple<Formats...>(arr, std::make_index_sequence<N>{});
}
Which you would use as:
std::tuple<Format...> the_line = as_tuple<Format...>(toks);

Tree in C++11 tuple with RTTI

I want to implement a simple tree in C++11 tuple with a Python fashion. In Python, we can use type(obj) to check run-time object type, and pass object with different type to one function, I have write pseudo code for calc(), how to do it in c++?
I try to print typeid(child1).name() and typeid(tree).name(), they are 'St5tupleIIciiEE' and 'St5tupleIIcS_IIciiEES0_EE'.
My environment is g++ 4.8.1. Thanks!
// pseudo code
int calc(tuple tree) {
symbol = type(get<0>(tree));
l_child = type(get<1>(tree));
r_child = type(get<2>(tree));
l = (type(l_child) == tuple) ? calc(l_child) : l_child;
r = (type(r_child) == tuple) ? calc(r_child) : r_child;
return l symbol r;
}
int main()
{
auto l_child = make_tuple('*', 1, 2);
auto r_child = make_tuple('-', 5, 1);
auto tree = make_tuple('+', l_child, r_child);
cout << calc(tree) << endl;
}
Python and C++ are very different languages. C++ is statically typed, Python is not. Transplanting Python techniques to C++ may or may not work. In this case it won't work.
In Python, there is only one tuple class, able to represent any tuple; in C++ there is an infinite number of tuple types, each one able to hold specific kinds of data. They are not interchangeable, as your experiment with typeid aptly demonstrates.
In C++, you cannot hold an arbitrary tree in a tuple. Write a tree class (or better, a class template).
Edit: technically, if you combine tuples with pointers and unions, you can get away with tuples. This is however not recommended. Your tree is going to be your central abstraction, exposing such low level details as pointers and unions is counterproductive and should be avoided. The C++ way is to write a class, stick to it.
It's unreal, since result of typeid().name is implementation-defined.
const char* name() const noexcept;
Returns: An implementation-defined ntbs.
However, here, you cannot use ternary operator, since calc(l_child) will be evaluated at compile-time, so if l_child is not tuple, compilation will be failed.
You can use some type-traits (or overloading), since tuple members are known at compile-time.
int calc(int value)
{
return value;
}
template<typename Left, typename Right>
int calc(const std::tuple<char, Left, Right>& tuple)
{
char symbol = std::get<0>(tuple);
Left l_child = std::get<1>(tuple);
Right r_child = std::get<2>(tuple);
int l = calc(l_child);
int r = calc(r_child);
return l /*symbol*/, r;
}
Live example

Convert STL map into a struct

I have a std::map<string, double> whose members look something like:
X = [{"N", 200}, {"sigma", 1.0}, {"T", .2}]
Now, given the struct foo Y:
struct foo {
int N;
double T;
};
Can I programmatically map the key/value pairs X -> Y without writing a custom class for each X -> Y type mapping? Note that:
X["sigma"] is not in Y, i.e. the mapping is not necessarily one-to-one
The type of Y.N is an int while X["N"] is a double.
I suspect the answer is no, unless some trickery is done at compile time.
Edit: It may not be clear what I'm looking for. A pseudo-code version for this example would look something like:
if("N" in X) -> Y.N = X["N"];
if("T" in X) -> Y.T = X["T"];
Or programmatically:
for key in Y:
if (key in X) -> Y.key = X[key]
No. C++ has no concept of reflection. At compile time, there's no "foo::N" string anymore. The compiler has converted all occurances of foo::N in the source code to a 0 byte offset within Foo objects. Also, you cannot enumerate class members at compile time.
You want to set a field in an object of structure type based on the value of key in the map's elements (which is a string). So, no it cannot be done at compile time.