In the process of learning the D language, I'm trying to make a generic Matrix class which supports type promotion of the contained object.
That is, when I multiply a Matrix!(int) to a Matrix!(real) I should get a Matrix!(real) as a result.
Since there are many different kinds of type promotions, reimplementing the opBinary method for every possible combination would be really tedious and a ton of boilerplate code. So mixins/mixin templates would seem to be the answer.
What I'm failing to understand is why this first code sample works
import std.stdio;
import std.string : format;
string define_opbinary(string other_type) {
return "
Matrix opBinary(string op)(Matrix!(%s) other) {
if(op == \"*\") {
Matrix result;
if(this.columns == other.rows) {
result = new Matrix(this.rows, other.columns);
} else {
result = new Matrix(0,0);
}
return result;
} else assert(0, \"Operator \"~op~\" not implemented\");
}
".format(other_type);
}
class Matrix(T) {
T[][] storage;
size_t rows;
size_t columns;
const string type = T.stringof;
this(size_t rows, size_t columns) {
this.storage = new T[][](rows, columns);
this.rows = rows;
this.columns = columns;
}
void opIndexAssign(T value, size_t row, size_t column) {
storage[row][column] = value;
}
mixin(define_opbinary(int.stringof));
mixin(define_opbinary(uint.stringof));
}
void main()
{
Matrix!int mymat = new Matrix!(int)(2, 2);
mymat[0,0] = 5;
writeln(mymat.type);
Matrix!uint mymat2 = new Matrix!(uint)(2, 2);
writeln(mymat2.type);
auto result = mymat * mymat2;
writeln("result.rows=", result.rows);
writeln("result.columns=", result.columns);
auto result2 = mymat2 * mymat;
writeln("result.type=",result.type);
writeln("result2.type=",result2.type);
}
the dub output:
Performing "debug" build using /usr/bin/dmd for x86_64.
matrix ~master: building configuration "application"...
Linking...
Running ./matrix.exe
50
00
int
uint
result.rows=2
result.columns=2
00
00
result.type=int
result2.type=uint
but the second code sample does not work
import std.stdio;
import std.string : format;
mixin template define_opbinary(alias other_type) {
Matrix opBinary(string op)(Matrix!(other_type) other) {
if(op == "*") {
Matrix result;
if(this.columns == other.rows) {
result = new Matrix(this.rows, other.columns);
} else {
result = new Matrix(0,0);
}
return result;
} else assert(0, "Operator "~op~" not implemented");
}
}
class Matrix(T) {
T[][] storage;
size_t rows;
size_t columns;
const string type = T.stringof;
this(size_t rows, size_t columns) {
this.storage = new T[][](rows, columns);
this.rows = rows;
this.columns = columns;
}
void opIndexAssign(T value, size_t row, size_t column) {
storage[row][column] = value;
}
mixin define_opbinary!(int);
mixin define_opbinary!(uint);
}
void main()
{
Matrix!int mymat = new Matrix!(int)(2, 2);
mymat[0,0] = 5;
writeln(mymat.type);
Matrix!uint mymat2 = new Matrix!(uint)(2, 2);
writeln(mymat2.type);
auto result = mymat * mymat2;
writeln("result.rows=", result.rows);
writeln("result.columns=", result.columns);
auto result2 = mymat2 * mymat;
writeln("result.type=",result.type);
writeln("result2.type=",result2.type);
}
the dub output:
source/app.d(60,19): Error: cast(Object)mymat is not of arithmetic type, it is a object.Object
source/app.d(60,27): Error: cast(Object)mymat2 is not of arithmetic type, it is a object.Object
source/app.d(64,20): Error: cast(Object)mymat2 is not of arithmetic type, it is a object.Object
source/app.d(64,29): Error: cast(Object)mymat is not of arithmetic type, it is a object.Object
/usr/bin/dmd failed with exit code 1.
What's extremely odd is that if I remove the mixin define_opbinary!(int); call, then I only get two arithmetic complaints (only the two complaints about line 60 (auto result = mymat * mymat2;) remain).
I have a feeling that somehow the compiler sees the two mixin calls as ambiguous and removes both but I'm not sure.
Any help would be greatly appreciated.
Oh I have a lot to say about this, including that I wouldn't use either type of mixin for this - I'd just use an ordinary template instead. I'll come back to that at the end.
I am going to try to be fairly comprehensive, so apologies if I describe stuff you already know, and on the other hand, I am probably going to give some irrelevant material too in the interests of providing comprehensive background material for a deeper understanding.
First, mixin vs template mixin. mixin() takes a string, parses it into a AST node (the AST btw is the compiler's internal data structure for representing code, it stands for "abstract syntax tree". foo() is an AST node like FunctionCall { args: [] }. if(foo) {} is one like IfStatement { condition: Expression { arg: Variable { name: foo }, body : EmptyStatement } - basically objects representing each part of the code).
Then it pastes that parsed AST node into the same slot where the mixin word appeared. You can often think of this as copy/pasting code strings, but with the restriction that the string must represent a complete element here, and it must be substituteable in the same context where the mixin was without errors. So like you can't do int a = bmixin(c) to make a variable with a b in front - the mixin must represent a complete node by itself.
Once it pastes in that AST node though, the compiler treats it as if the code was all written there originally. Any names referenced will be looked up in the pasted context, etc.
A template mixin, on the other hand, actually still has a container element in the AST, which is used for name lookups. It actually works similarly to a struct or class inside the compiler - they all have a list of child declarations that remain together as a unit.
The big difference is that a template mixin's contents are automatically accessible from the parent context... usually. It follows rules similar to class inheritance, where class Foo : Bar can see Bar's members as if they are its own, but they still remain separate. You can still do like super.method(); and call it independently of the child's overrides.
The "usually" comes in because of overloading and hijacking rules. Deep dive and rationale here: https://dlang.org/articles/hijack.html
But the short of it is in an effort to prevent third party code from silently being able to change your program's behavior when they add a new function, D requires all sets of function overloads to be merged at the usage point by the programmer, and it is particularly picky about operator overloads since they already have a default behavior that any mixin is going to be modifying.
mixin template B(T) {
void foo(T t) {}
}
class A {
mixin B!int;
mixin B!string;
}
This is similar to the code you have, but with an ordinary function. If you compile and run, it will work. Now, let's add a foo overload directly to A:
mixin template B(T) {
void foo(T t) {}
}
class A {
mixin B!int;
mixin B!string;
void foo(float t) {}
}
If you try to compile this with a string argument, it will actually fail! "Error: function poi.A.foo(float t) is not callable using argument types (string)". Why won't it use the mixin one?
This is a rule of template mixins - remember the compiler still treats them as a unit, not just a pasted set of declarations. Any name present on the outer object - here, our class A - will be used instead of looking inside the template mixin.
Hence, it sees A.foo and doesn't bother looking into B to find a foo. This is kinda useful for overriding specific things from a template mixin, but can be a hassle when trying to add overloads. The solution is to add an alias line to the top-level to tell the compiler to specifically look inside. First, we need to give the mixin a name, then forward the name explicitly:
mixin template B(T) {
void foo(T t) {}
}
class A {
mixin B!int bint; // added a name here
mixin B!string bstring; // and here
alias foo = bint.foo; // forward foo to the template mixin
alias foo = bstring.foo; // and this one too
void foo(float t) {}
}
void main() {
A a = new A;
a.foo("a");
}
Now it works for float, int, and string.... but it also kinda defeats the purpose of template mixins for adding overloads. One trick you can to is to put a top-level template function in A, and it just forwards to the mixins... just they need a different name to register.
Which brings me back to your code. Like I said, D is particularly picky about operator overloads since they always override a normal behavior (even when that normal behavior is an error, like in classes). You need to be explicit about them at the top level.
Consider the following:
import std.stdio;
import std.string : format;
mixin template define_opbinary(alias other_type) {
// I renamed this to opBinaryHelper since it will not be used directly
// but rather called from the top level
Matrix opBinaryHelper(string op)(Matrix!(other_type) other) {
if(op == "*") {
Matrix result;
if(this.columns == other.rows) {
result = new Matrix(this.rows, other.columns);
} else {
result = new Matrix(0,0);
}
return result;
} else assert(0, "Operator "~op~" not implemented");
}
}
class Matrix(T) {
T[][] storage;
size_t rows;
size_t columns;
const string type = T.stringof;
this(size_t rows, size_t columns) {
this.storage = new T[][](rows, columns);
this.rows = rows;
this.columns = columns;
}
void opIndexAssign(T value, size_t row, size_t column) {
storage[row][column] = value;
}
mixin define_opbinary!(int);
mixin define_opbinary!(uint);
// and now here, we do a top-level opBinary that calls the helper
auto opBinary(string op, M)(M rhs) {
return this.opBinaryHelper!(op)(rhs);
}
}
void main()
{
Matrix!int mymat = new Matrix!(int)(2, 2);
mymat[0,0] = 5;
writeln(mymat.type);
Matrix!uint mymat2 = new Matrix!(uint)(2, 2);
writeln(mymat2.type);
auto result = mymat * mymat2;
writeln("result.rows=", result.rows);
writeln("result.columns=", result.columns);
auto result2 = mymat2 * mymat;
writeln("result.type=",result.type);
writeln("result2.type=",result2.type);
}
I pasted in the complete code, but there's actually only two changes there: the mixin template now defines a helper with a different name (opBinaryHelper), and the top-level class now has an explicit opBinary defined that forwards to said helper. (If you were to add other overloads btw, the alias trick from above may be necessary, but in this case, since it is all dispatched on if from inside the one name, it lets you merge all the helpers automatically.)
Finally, the code works.
Now, why wasn't any of this necessary with the string mixin? Well, back to the original definition: a string mixin parses it, then pastes in the AST node /as if it were originally written there/. That latter part lets it work (just at the cost of once you mixin a string, you are stuck with it, so if you don't like part of it, you must modify the library instead of just overriding a portion).
A template mixin maintains its own sub-namespace to allow for selective overriding, etc., and that triggers a foul with these stricter overloading rules.
And finally, here's the way I'd actually do it:
// this MatrixType : stuff magic means to accept any Matrix, and extract
// the other type out of it.
// a little docs: https://dlang.org/spec/template.html#alias_parameter_specialization
// basically, write a pattern that represents the type, then comma-separate
// a list of placeholders you declared in that pattern
auto opBinary(string op, MatrixType : Matrix!Other_Type, Other_Type)(MatrixType other) {
// let the compiler do the promotion work for us!
// we just fetch the type of regular multiplication between the two types
// the .init just uses the initial default value of the types as a placeholder,
// all we really care about is the type, just can't multiply types, only
// values hence using that.
alias PromotedType = typeof(T.init * Other_Type.init);
// in your version, you used `if`, but since this is a compile-time
// parameter, we can use `static if` instead and get more flexibility
// on stuff like actually changing the return value per operation.
//
// Don't need it here, but wanted to point it out anyway.
static if(op == "*") {
// and now use that type for the result
Matrix!PromotedType result;
if(this.columns == other.rows) {
result = new Matrix!PromotedType(this.rows, other.columns);
} else {
result = new Matrix!PromotedType(0,0);
}
return result;
// and with static if, we can static assert to turn that runtime
// exception into a compile-time error
} else static assert(0, "Operator "~op~" not implemented");
}
Just put that opBinary in your class and now the one function can handle all the cases - no need to list specific types, so no more need for mixin magic at all! (....well unless you need virtual overriding with child classes, but that's a whole other topic. Short tip tho, it is possible to static foreach that, which I talked about in my last SO answer here: https://stackoverflow.com/a/57599398/1457000 )
There's a few D tricks in that little function, but I tried to explain in the comments of the code. Feel free to ask if you need more clarification though - those : patterns in template are IMO one of the more advanced D compile-time reflection things, so they're not easy to get at first, but for simple cases like this, it kinda makes sense, just think of it as a declaration with placeholders.
Related
I have 2 issues in a template class I'm building. I've included example code below. First question is whether I can coerce the auto type deducted for a templated class. i.e.:
auto p = myvar;
where myvar is T<...>, could I force auto to detect Q<...>? This is simplified. Read on for a more clear explanation.
Edited for clarity: Let me explain what I'm doing. And I'd also like to indicate that this style code is working on a large-scale project perfectly well. I am trying to add some features and functions and in addition to smooth out some of the more awkward behaviors.
The code uses templates to perform work on n-dimensional arrays. The template has a top-level class, and a storage class underneath. Passing the storage class into the top level class allows for a top level class which inherits the storage class. So I start with NDimVar, and I have NDimStor. I end up with
NDimVar<NDimStor>
The class contains NO DATA except for the buffer of data:
class NDimStor<size_t... dimensions> {
int buffer[Size<dimensions...>()]
}
This makes the address of the class == the address of the buffer. This is key to the whole implementation. Is this an incorrect assumption? (I can see this works on my system without any issues, but perhaps this isn't always the case.)
When I create NDimVar<NDimStor<10,10>> I end up with a 10x10 array.
I have functions for getting pieces of the array, for example:
NDimVar<NDimStor<dimensions...>>::RemoveDim & get(int index);
This creates a new 1d array of 10 elements out of the 2d 10x10 array:
NDimVar<NdimStor<10>>
In order to return this as a reference, I use a reinterpret_cast at the location of the data I want. So in this example, get(3) would perform:
return reinterpret_cast<NDimVar≤NDimStor<dimensions...>>::RemoveDim&>(buffer[index * DimensionSumBelow<0>()]);
DimensionSumBelow<0> returns the sum of elements at dimensions 1+, i.e. 10. So &buffer[30] is the address of the referenced 1d NDimVar.
All of this works very well.
The only issue I have is that I would like to add on overlays. For example, be able to return a reference to a new class:
NDimVar<NDimPermute<NDimStor<10,10>,1,0>>
that points to the same original location along with a permutation behavior (swapping dimensions). This also works well. But I would like for:
auto p = myvar.Permute<1,0>()
to create a new copy of myvar with permuted data. This would work if I said:
NDimVar<NDimStor<10,10>> p = myvar.Permute<1,0>().
I feel that there is some auto type deduction stuff I could do in order to coerce the auto type returned, but I'm not sure. I haven't been able to figure it out.
Thanks again,
Nachum
What I want is:
1. Create temporary overlay classes on my storage, e.g. A_top<A_storage> can return a type called A_top<A_overlay<A_storage>> without creating a new object, it just returns a reference to this type. This changes the way the storage is accessed. The problem is upon a call to auto. I don't want this type to be instantiated directly. Can I modify the return to auto to be an original A_top?
#include <iostream>
using namespace std;
class A_storage {
public:
float arr[10];
A_storage () {
}
float & el (int index) {
return arr[index];
}
};
template <typename T> class A_overlay : T {
private:
A_overlay () {
cout << "A_overlay ()" << endl;
}
A_overlay (const A_overlay &) {
cout << "A_overlay (&)" << endl;
}
public:
using T::arr;
float & el (int index) {
return arr[10 - index];
}
};
template <typename T> class A_top;
template <typename T> class A_top : public T {
public:
A_top () {
}
A_top<A_overlay<A_storage>> & get () {
return reinterpret_cast<A_top<A_overlay<A_storage>>&>(*this);
}
};
using A = A_top<A_storage>;
int main (void) {
A a;
auto c = a.get(); // illegal - can i auto type deduce to A_top<A_storage>?
return 0;
}
If a function accepts (A_top<A_storage> &) as a parameter, how can I create a conversion function that can cast A_top<A_overlay<A_storage>>& to A_top<A_storage>& ?
Thanks,
Nachum
First, your design doesn't look right to me, and I'm not sure if the behaviour is actually well-defined or not. (Probably not.)
In any case, the problem is not with auto. The error is caused by the fact that the copy constructor of A_overlay is private, while you need it to copy A_top<A_overlay<A_storage>> returned by a.get() to auto c.
(Note that the auto in this case obviously gets deduced to A_top<A_overlay<A_storage>>, I assume you made a typo when said that it's A_top<A_storage>.)
Also note that A_storage in A_top::get() should be replaced with T, even if it doesn't change anything in your snippet because you only have T == A_storage.
If a function accepts (A_top &) as a parameter, how can I create a conversion function that can cast A_top> to A_top& ?
Ehm, isn't it just this:
return reinterpret_cast<A_top<A_storage>&>(obj);
reinterpret_cast should almost never be used. It essentially remove any compiler validation that the types are related. And doing unrelated cast is essentially undefined behavior as it essentially assume that derived classes are always at offset 0...
It does not make any sense to write such code. It is not maintainable and hard to understand what you are trying to achieve. It look like you want to pretend that your A_top<A_storage> object is a A_top<A_overlay<A_storage>> object instead. If this is what you want to do, then declare A alias as that type.
In your code, it look like you want to invert the indexing so that item at position 10 is returned when you ask item at position 0 and vice versa. Do you really think, that it is obvious from your obfuscated code? Never write such bad code.
Something like
class A_overlay {
public:
float & el (int index) { return arr[10 - index]; }
private:
A_storage arr;
};
would make much more sense than your current code.
No cast needed.
Easy to understand.
Well defined behavior.
You might keep your job.
And obviously, you would update the following line as appropriate:
using A = A_top<A_storage>;
Also, if A_top has no useful purpose, then why not using A_overlay directly? And why are you using template if A_storage is not a template? Do you really want to reuse such mess elsewhere in your code base.
Obviously, your code inheritance does not respect IS-A relationship if your write such code. So it is clearly a bad design!
I'm working with the following class design and would like to get rid of the same for-loop in each of the forwarding method calls by using some sort of delegate/member pointer. Is this somehow possible?
class Type
{
void func_v();
// more methods ...
bool func_b();
// ...
unsigned func_u();
// ...
}
class MultiType : public Type
{
void func_v() override
{
for(Type* type : _typeVec)
type->func_v();
}
bool func_b() override
{
bool result = true;
for(Type* type : _typeVec)
result = result && type->func_b();
return result;
}
unsigned func_u() override
{
int count = 0;
for(Type* type : _typeVec)
count += type->func_u();
return count;
}
protected:
std::vector<Type*> _typeVec;
}
What I'm looking for is something similar to this:
class MultiType : public Type
{
void applyMember(MemberType member)
{
for(Type* type : _typeVec)
// how to deal with varying parameters and parameter types here?
type->member(...)
// how to deal with varying return values and processing strategies to combine those?
}
void func_v() override
{
applyMember(&Type::func_v);
}
bool func_b() override
{
applyMember(&Type::func_b);
}
unsigned func_u() override
{
applyMember(&Type::func_u);
}
protected:
std::vector<Type*> _typeVec;
}
Since all your example functions perform different actions I would recommend you to look into accumulate and for_each algorithms.
You could replace your raw loops with something like this:
bool func_b() override
{
return std::accumulate(_typeVec.begin(), _typeVec.end(), true, [](const bool &result, const Type *t) {
return result && type->func_b();
});
}
void func_v() override
{
std::for_each(_typeVec.begin(), _typeVec.end(), [](Type *type){type->func_v()});
}
The "What I'm looking for..." part in your question doesn't work. For example, func_b doesn't return any value. Even if you could do something like that using a combination of template functions and lambdas, you'd have the same independent parts in all functions (local variable declaration, reduction mechanism, return statement). The only thing you could abstract away is the for loop, but that's just a single line you'll be replacing with a different line.
There's no point doing that.
From what I understand of your question, you would want to call the same method, and possibly use the same parameters, on each object.
For example, in your production code, func_v would take parameters. This may be different from func_b that may not take parameters.
Let us assume that func_v takes in a int.
In this case, would this be true?
MultiType::func_v(int value) {
for (Type* type : _typeVec)
type->func_v(value); //note: same value being passed to all
}
You may want to use functors here. However, I don't think that will work well with multiple return values.
Functors can be used to aggregate values. Is that what you want to do?
Forgetting abstracting the loop out, how will you return multiple values from func_v above?
So if returning multiple values is not a problem, functors can be what you want to do.
It may be too much work for too little though. You will have to find out if it is worth it in your production code.
I would like to share a function across template instantiations, and wonder whether there's a way to do that.
_____ edit to clarify question _____
Lets look at the following example
#include <array>
template <unsigned int K>
class kd_tree
{
public:
using kd_point = std::array<float, K>;
bool isValid(const kd_point &kdPoint) const
{
for (unsigned i = 0; i < K; i++)
if ( isnan(kdPoint[i]) ||
kdPoint[i] == numeric_limits<float>::infinity() ||
kdPoint[i] == -numeric_limits<float>::infinity() )
return false;
return true;
}
};
for each value of K, a new value of isValid will be created that differs by nothing except the concrete value of K. It certainly doesn't need K to use kd_point's operator[]
Question is whether there's a way to take isValid out of the class and make it a function that accepts a kd_point & a K as parameters, and check the point, thus saving in executable size on multiple copies of it, e.g. one for K=2 & another for K=3.
I'm not sure I've understood the question, but could a factory like the following one solve the problem?
void fn() { }
template<void(*Function)()>
class C {
C() = default;
public:
static C<Function>* create() {
return new C<Function>{};
}
};
using CfnFactory = C<fn>;
int main() {
C<fn> *c = CfnFactory::create();
}
Note that the example above applies well also with member functions, it's only a minimal example of a possible solution from which to start (of course, if I got the problem).
The idea is to inject in the factory the function to be used to create instances of your type, so that all of them will rely on the same function. This way you can also create multiple factories based on different functions and mix together the returned objects (the last point requires a base, non-template class, but it's quite easy to do).
In any case, I strongly suggest you to reduce the amount of code in your example to a mvce.
It would help the reader and it will increase the chances to get a valid response.
I can compose an AB struct that has all the members of structs A and B:
template AFields() {int a;}
struct A { mixin AFields; }
template BFields() {int b;}
struct B { mixin BFields; }
struct AB { mixin AFields; mixin BFields; }
A a; a.a = 1;
B b; b.b = 2;
AB ab; ab.a = 3; ab.b = 4;
But how can I construct AB, if I don't have control over A and B and I don't have AFields and BFields? I.e. how to write the CatStruct template so the code below compiles?
struct A { int a; }
struct B { int b; }
mixin CatStruct!("AB", A, B);
AB ab;
ab.a = 1; ab.b = 2;
The standard library has a few hidden jewels that I didn't actually even know about myself until I peeked at the source to answer this question:
http://dlang.org/phobos/std_traits.html#Fields
and the ones right under it too. With these, we can make your CatStruct fairly succinctly. Behold:
mixin template CatStruct(string name, T...) {
static import std.traits, std.conv;
private string _code_generator() {
string code = "struct " ~ name ~ " {";
foreach(oidx, t; T) {
foreach(idx, field; std.traits.FieldTypeTuple!t)
// this line is a monster, see the end of this answer
code ~= "std.traits.FieldTypeTuple!(T["~std.conv.to!string(oidx)~"])["~std.conv.to!string(idx)~"] "~ std.traits.FieldNameTuple!t[idx] ~ ";";
}
code ~= "}";
return code;
}
mixin(_code_generator());
}
This uses a string mixin though... and while string mixins can do basically anything, they also basically suck. This is liable to be brittle but I think it will basically work while basically sucking.
It also won't do struct methods, but I think that's too hard to realistically do with any of these magical things, except perhaps opDispatch, as seen in the other answer (which is pretty nice btw, don't take my answer as a repudiation of that one, just another idea).
If there's clashing names between the two structs too, they will break this, and you will get a hideously ugly error message out of the compiler. With a real template mixin, there's an easy fix for that - a named template mixin, which allows you to disambiguate. But no such thing here. I guess you could hack one in if you needed it.
But anyway, there might be a way to use those FieldTypeTuple and FieldNameTuple from the stdlib to do this even nicer, but I think it is more-or-less what you're asking for now.
BTW, I'd say just do ordinary composition if you at all can, it is going to work the best in general. (Don't forget about alias this too which can do automatic forwarding to member variables.)
If you haven't done a lot of mixins, you probably want to ask my why I used that crazy string in the code ~= part instead of the more straightforward. code ~= field.stringof ~ " "~ FieldNameTuple!t[idx] ~ ";";
tl;dr: just trust me, ALWAYS use local names available to the scope where you run the mixin() itself in the code you generate. Long explanation follows/
It has to do with name clashes and symbol lookups. I used static imports and fully qualified names in the mixed in code - including using the local symbol for the FieldTypeTuple rather than field.stringof - to keep this as namespace-tidy as possible.
Consider the case where struct A imports some other module internally and defines a field with it.
// using my color.d just cuz I have it easily available
// but it could be anything, so don't worry about downloading it
struct A { import arsd.color; Color a; }
AB ab;
import arsd.color;
ab.a = Color.white; ab.b = 2; // we expect this work, should be the same type
Since that's a local import inside struct A, the name is meaningless at the point of the mixin.
Go ahead and adjust the mixin so it compiles using the simple line
// comment fancy line
// code ~= "std.traits.FieldTypeTuple!(T["~std.conv.to!string(oidx)~"])["~std.conv.to!string(idx)~"] "~ std.traits.FieldNameTuple!t[idx] ~ ";";
// paste in simple line
code ~= field.stringof ~ " "~ std.traits.FieldNameTuple!t[idx] ~ ";";
And compile:
$ dmd f.d ~/arsd/color.d
f.d-mixin-31(31): Error: undefined identifier 'Color'
f.d(4): Error: mixin f.CatStruct!("AB", A, B) error instantiating
Zoinks! It had no idea what the string "Color" was supposed to refer to. If we imported some other kind of struct Color in the local module, it would compile.... but then it would refer to a different type:
struct A { import arsd.color; Color a; }
struct B { int b; }
struct Color { static Color white() { return Color.init; } }
mixin CatStruct!("AB", A, B);
AB ab;
import arsd.color;
ab.a = Color.white; ab.b = 2;
Compile it and see a silly sounding error:
$ dmd f.d ~/arsd/color.d
f.d(12): Error: cannot implicitly convert expression (white()) of type Color to Color
BTW: remember this if you ever see it in the wild - the compiler error message sounds absurd, "cannot implicitly convert Color to Color", but it actually does have a logical meaning: there's just two different types with the same name in different modules.
Anyway, it sounds silly, but makes sense because the two scopes imported different structs.
With the long-form FieldTypeTuple used with a local static import, it always refers to the actual type passed in. Indirectly, sure, but also unambiguously.
I apologize to those reading this who already know about the pitfalls of string mixins, but anyone finding this on a search might not know why I used that convoluted code. It is complex due to real world experience with actual problems, I swear! :) It is a lot easier to do it right the first time than try to debug the weird nonsense down the road it can bring doing it the other way.
There's a lot of ground to cover here (members, functions, templates, ect.).
However, here's an idea to get you started:
import std.typecons;
struct A { int a; }
struct B { int b; }
struct AB
{
mixin MultiProxy!(A, B);
}
mixin template MultiProxy(A, B) {
private A _a;
private B _b;
mixin Proxy!_a aProxy;
mixin Proxy!_b bProxy;
template opDispatch(string op) {
static if (is(typeof(aProxy.opDispatch!op))) {
alias opDispatch = aProxy.opDispatch!op;
}
else {
alias opDispatch = bProxy.opDispatch!op;
}
}
}
unittest
{
AB ab;
ab.a = 4;
ab.b = 5;
assert(ab.a == 4);
assert(ab.b == 5);
}
I haven't had time to thoroughly test this, so I wouldn't be suprised if there are a number of areas where it falls over (just look at the implementation of Proxy to see all the things it has to take into account).
However, the general idea is to create two proxies, each explicitly named (aProxy,bProxy) so we can explicitly call the opDispatch of either one depending on which will compile.
In the interest of completeness, here's a solution that uses named tuples:
import std.meta, std.traits, std.typecons;
// helper template to interleave 2 alias lists
template Interleave(A...)
{
static if(A.length == 0)
alias A Interleave;
else
alias AliasSeq!(A[0], A[A.length/2],
Interleave!(A[1..A.length/2], A[A.length/2+1..$])) Interleave;
}
// helper template to produce tuple template parameters
template FieldTypeNameTuple(A)
{
alias Interleave!(Fields!A, FieldNameTuple!A) FieldTypeNameTuple;
}
template CatStruct(A...)
{
alias Tuple!(staticMap!(FieldTypeNameTuple, A)) CatStruct;
}
// usage
struct A { int a; }
struct B { int b; }
struct C { int c; }
alias CatStruct!(A, B, C) ABC;
I've got way too much information to work with, so for now I'll consider this question answered until I can sort it all out and decide on the final implementation! Thanks a ton gf and Simon Buchan. I wish I could accept both of your answers, since they're both definite possibilities!
Additional / Revised Conceptual Information as suggested:
What I am aiming to do;
I am making a game. In this game every object used is an instance of the DOBJ class. The TUR class extends the DOBJ class. The SHO class extends the TUR class.
Each TUR class has an array of SHO's stored in it's SHOARR array. Each SHO instance needs to be given a set of instructions.
I know for a fact I could make 1000's of different SHO classes that have their instructions set during construction.
However, considering I will have so many different acting SHO instances, I was interested in another way to pass a set of instructions. Through the contruction of the SHO would be the ideal.
The instructions I am attempting to pass to each SHO are simple if statements;
if(frame > 64) { rotation += 4; };
if(state == 0 && frame < 32) { xs = 12; ys = 12; state = 1; };
Original question
Migration from ActionScript3.0 to C++ is proving to be a trial indeed. Thanks to those who have answered my questions thus far and also to those who opened stackoverflow in the first place. Onto the question... (TL;DR near the bottom to get straight to the question)
I'm attempting to apply the same logic that I could apply in AS3.0 to my project in C++ and it's just not going very well.
In AS3.0 I was used to slapping any and every datatype into an Array. It made things pretty simple. Now that I've run into C++ dev, I realized that I can't exactly do that anymore.
So now I'm stuck with this problem of rewriting a little AI system in a new language, where the driving point of the system isn't even compatible!
Here's an example of a piece of the code I was writing in AS3.0;
AI[NUM][1]( OBJ, AI[NUM][2], AI[NUM][3] );
AI being an array, NUM being an integer, OBJ being an instance of a class.
This line obviously called the function in the second element of the first array in the main array with the arguments being a class in which to perform the function on, whatever was in the third element of the first array of the main array, and likewise the fourth element.
In this case;
AI[NUM][1] would be a function
AI[NUM][2] would be a variable
AI[NUM][3] would be a number
Generally, my AI was run on calling a function to change or compare the variable with a number.
An example would be;
CompareST( someObject, "x", 500 );
and return true if someObject's x variable was smaller than (ST) 500.
The AI array itself was just filled with arrays of calls similar to this.
Quite new to C++ I had no idea how to go about this, so I did a bit of searching and reading of many different websites and came to the conclusion that I should look into function pointers.
However, after reading a bit into them, I've come to the conclusion that it won't help me realize my goal. While it did help me call functions like I wanted to call them, it doesn't help me stack different datatypes into one large array of arrays.
TL;DR
EDIT++:
What I need for each object is a set of instructions to be checked every frame. However, for each instance of the class, the instructions have to be different.
I plan on having a LOT of different instances, so making a class for each one is unreasonable.
Thus, I needed a way to pass a set of instructions to each one through it's constructor and read + execute them at any time their think() function is called.
My ultimate goal (aside from finding out about a better way to go about this) would be to be able to have an array of function calls, like;
A[n][0]( O, A[n][1], A[n][2] );
Where;
O is the instance the function is altering
A[n][0] is a function (Equality or Comparison)
A[n][1] is the variable, eg; "x", O["x"] (or a pointer to that variable in the case of C++)
A[n][2] is the value to alter the variable by, or compare it to.
And I'm not sure how I would rewrite this into C++, or alter it to work in another way.
Aftermath / Additional Information
What I'm actually aiming to do is be able to give an object a set of instructions at the time of it's creation, through the constructor. For example upon creation give an object instructions to wait 64 frames, and then rotate in the opposite direction, would have been something like this;
t.AI = [ [ 1, AIF.CompareET, "STATE", 0, AIF.CompareGT, "FRAME", 64, 0, AIF.EqualityAT, "baseRotation", 180, AIF.EqualityET, "STATE", 1 ] ];
In pseudocode;
(The 1 in the array denotes how to read the rest of the array, in this case everything before the odd 0 [ The one that comes after 64 ] is a comparison. If any of those fail, anything after the 0 will not be looked at )
Compare STATE is equal to (ET) 0, if true
Compare FRAME is greather than (GT) 64, if true
Add 180 to (AT) baseRotation, Set STATE equal to 1
Sorry that this turned out really long. I hope it's understandable, and I'm not asking something stupidly difficult to explain.
You can store functions using function pointers or functors. Variant types though are not natively supported by C++, you have to use custom solutions there.
One possibility would be to use Boost.Any (or better, Boost.Variant if you only use a fixed set of types):
typedef void (*Function)(Object*, const std::string&, boost::any&);
std::vector<Function> functions;
Given some function:
void f(Object* obj, const std::string& name, boost::any& value) {
// ...
}
you could store and call it similar to your example:
functions.push_back(&f);
functions[0](obj, "x", boost::any(500));
To utilize a declarative syntax, there are three options that come to my mind:
you use a similar approach and have central "interpreter" function, e.g. based on a switch (don't forget to switch to integers or pointers-to-members instead of strings if you need performance)
you invent your own language and generate C++ code from description files
you compose function objects in a declarative way
To do composition, you could use Boost.Bind or something like custom objects that represent operations:
struct Operation {
virtual ~Operation() {}
virtual bool operator()(Object&) = 0;
};
template<class T>
struct GreaterThen : Operation {
typedef T Object::*Member;
Member member;
const T value;
CompareGT(Member member, const T& value) : member(member), value(value) {}
bool operator()(Object& obj) { return (obj.*member > value); }
};
template<class T>
struct SetTo : Operation {
typedef T Object::*member;
Member member;
const T value;
SetTo(Member member, const T& value) : member(member), value(value) {}
bool operator()(Object& obj) { obj.*member = value; return true; }
};
Now we can build operation lists:
typedef std::vector<Operation*> OpList;
OpList operation;
operations.push_back(new GreaterThen<int>(&Object::Frame, 64));
operations.push_back(new SetTo<int>(&Object::State, 1));
We can use helper functions to avoid having to specify the template types:
template<class T>
Operation* opGreaterThen(T Object::*mem, const T& val) {
return new GreaterThen<T>(mem, val);
}
Assuming a similar helper for SetTo and using Boost.Assign the above becomes:
OpList operations = boost::assign::list_of
(opGreaterThen(&Object::Frame, 64))
(opSetTo (&Object::State, 1));
Executing the operations becomes the following then:
OpList::iterator it = operation.begin();
for( ; it != operations.end(); ++it) {
Operation& op = *it; // just for readability
if(!op(someObject)) break; // stop if operation returns false
}
Wow.
Reading through that slowly suggests what you're trying to end up with is an array of function calls and you can choose a different function with the same parameters (but different implementation) for different actions and choose the correct one for the correct case.
If that is the case, you're looking for function pointers. Try this tutorial.
You should be able to use a function pointer with an argument set and point it to the correct function based on your needs. You won't need an array of function pointers for this either - any function that matches the definition should do. From the tutorial, declare a function pointer like this:
int (TMyClass::*functptr)(classname, int, int) = NULL; // C++
Then assign it later:
this.functptr = &TMyClass::doitthisway;
While it is possible (although a pain) to have an array of arbitrary types, you pretty much never need it, since you have to know something about what is where to do anything interesting with it: for example, your 'TL;DR' example seems to look something like:
struct AIRule {
// Can only handle comparing ints, see later for more general solution.
typedef bool compare_type(AIObject*, AIObject::*int, int);
compare_type* compare;
AIObject* object;
AIObject::int* member;
int comparand;
};
So now you can do something like:
bool ai_equal(AIObject* object, AIObject::int* member, int comparand) {
return object->*member == comparand;
}
...
ai[n].compare = &ai_equal;
ai[n].object = some_object;
ai[n].member = &AIObject::some_member;
ai[n].comparand = 50;
...
if (ai[n].compare(ai[n].object, ai[n].member, ai[n].comparand)) {
...
}
This just moves the any type problem from the rules array to member though. C++ needs to know at least how many bytes a member is, and a string (for example) can be much bigger than an int. You can get around this by using pointers: which essentially is C++'s version of any, but you then need to delete it yourself (or you will leak memory!), at which point the interface method below becomes simpler.
If I was doing what you seem to want, I would use inheritance:
struct Sprite {
int frame;
double rotation;
Sprite() {
frame = 0;
rotation = 0.0;
}
virtual ~Sprite() {}
virtual void think() {
++frame;
}
virtual void draw() {
...
}
};
struct RotatingSprite : public Sprite {
int state;
MyShape() {
state = 0;
}
void think() {
Sprite::think();
if (state == 0 && frame > 64) {
state = 1;
rotation += 180.0;
}
}
};
Or a function pointer:
struct Sprite {
int frame;
double rotation;
void (*think)(Sprite*);
Sprite() {
frame = 0;
rotation = 0.0;
}
};
void rotate_think(Sprite* sprite) {
if (sprite->state == 0 && sprite->frame > 64) {
sprite->state = 1;
sprite->rotation += 180.0;
}
}
...
sprite->think = &rotate_think;
If you really need to do it dynamically I would recommend using the ++ part of C++. For the predicates (a predicate is just something that returns a boolean, like isLowerCase()) create an AIPredicate interface, and the actions an AIAction interface:
struct AIPredicate {
// "When you delete an AIPredicate, delete the full type, not just this interface."
virtual ~AIPredicate() {}
// "You can treat this as a function (operator()) but I'm not providing an implementation here ( = 0)"
virtual bool operator()(AIObject* object) = 0;
};
struct AIAction {
virtual ~AIAction() {}
virtual void operator()(AIObject* object) = 0;
};
struct AIRule {
// std::auto_ptr (or std::unique_ptr if you can use C++0x) will delete predicate for you.
// Add "#include <memory>" to your includes if it complains (most std headers will include it already)
std::auto_ptr<AIPredicate> predicate;
std::auto_ptr<AIAction> action;
};
Now you can make types like:
struct AIFrame : public AIPredicate {
// Implement the operator() member AICondition promises.
bool operator()(AIObject* object) {
return object->foo < 100;
}
};
...
// Use .reset() instead of = if you use std::unique_ptr.
ai[n].predicate = new AIFooIsLow();
If you want to have a very general predicate type, you can use the very powerful (and complicated) templates feature:
// The naming convention I'm using here is 'T'TitleCase for template parameters, TitleCase for types,
// lower_case for arguments and variables and '_'lower_case for members.
template<typename TMemberType, AIObject::TMemberType* TMember>
struct AIMemberEquals : public AIPredicate {
// Constructor: Initializes a new instance after it is created.
AIMemberEquals(TMemberType comparand) {
// Save comparand argument so we can use it in operator().
_comparand = comparand;
}
bool operator()(AIObject* object) {
return object->*TMember == comparand;
}
// Stores the value to compare.
TMemberType _comparand;
};
Unfortunately, creating templates looks a bit crazy:
ai[n].predicate = new AIMemberEquals<int, &AIObject::some_member>(100);
Read it as "create a new instance of (the type that AIMemberEquals applied to int and (the some_member member of AIObject) creates), with the argument 100".
When you have multiple predicates memory management becomes a bit more difficult without C++0x's unique_ptr or shared_ptr, types that will delete the object for you, since std::auto_ptr doesn't work in containers:
#include <vector>
struct AIData {
// vector is fairly close to AS3's Array type, it is a good default for
// arrays of changing or unknown size.
std::vector<AIPredicate*> predicates;
// Destructor: will be run before the memory for this object is freed.
~AIData() {
for (int i = 0; i != predicates.size(); ++i) {
delete predicates[i];
}
}
};
...
ai[n].predicates.push_back(new AIFooIsLow());
...
for (int i = 0; i != ai[n].predicates.size(); ++i) {
(*ai[n].predicates[i])(ai[n].object);
}
In C++0x:
struct AIData {
// unique_ptr will delete it for you, so no ~AIData() needed.
std::vector<unique_ptr<AIPredicate>> predicates;
};
Your final example could in C++ look something like:
std::auto_ptr<Shape> shape(new Shape());
...
std::auto_ptr<AIRule> rule(new AIRule());
rule->predicates.push(new AIMemberEquals<int, &Shape::state>(0));
rule->predicates.push(new AIMemberGreater<int, &Shape::frame>(64));
rule->actions.push(new AIAddMember<double, &Shape::rotation>(180.0));
rule->actions.push(new AISetMember<int, &Shape::state>(1));
shape->ai.push(rule); // .push(std::move(rule)); if you are using unique_ptr
Certainly not as pretty, but it works and is fairly flexible.