Since we want semantic macros we need to consider the usecase where C uses macros to create generic code. Here is a sketch of how that could be implemented.
1. A generic implementation is implemented in a generics module:
module vector (A, B, C); // A, B, C are generic parameters.
type Foo struct {
A a;
}
func C test(B b, Foo *foo) {
return a + b;
}
To use this generic code, simply import:
import vector(f64, f32, i32) as generic_tests;
func f64 test()
{
generic_tests.Foo foo = { 2.0 };
return generic_tests.test(3.0, foo);
Thanks to the namespacing in C2, we can don't actually need to create an alias if we want to. The module is all we need in order to make this work.
One issue
There is only one issue here. This solution has pretty much the same issue as C++ templates – lack of good error messages.
A solution would therefore be to set up a clause with contracts the types must pass. This is similar to the solution proposed in C++ / Go2:
module vector (A, B, C);
contract(A a, B b, C c) {
a + b; // Only expressions allowed
c == a + b; // Only expressions allowed
}
... code ...
A generic is not completely error checked until it is instantiated, with the contract however, the expressions are FIRST validated, and reported as errors.
import vector(struct Bar, f32, i32) as gen_test;
// This would give the error
---> Illegal arguments for generic module vector, breaks contract 'struct Bar' == 'f32' + 'i32'
This proposal is minimal, does not clutter the language with type arguments (i.e. no Foo<f32> etc), prevents misuse and requires very little extra syntax and complexity.