www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Error using templates: "cannot use template to add field to aggregate

reply pineapple <meapineapple gmail.com> writes:
I'm getting several of these since I'm trying to do the same 
thing in a few places. Here's a complete error:

     path\to\file.d(55): Error: variable 
units.unitvalue.opmethod!("sum(in unitvalue value)", 
"add(value)").methodtemplate cannot use template to add field to 
aggregate 'unitvalue'
     path\to\file.d(127): Error: template instance 
units.unitvalue.opmethod!("sum(in unitvalue value)", 
"add(value)") error instantiating

In this class I wrote a number of methods like "multiply", 
"divide", "add", "subtract" and I want to add alternatives that 
don't modify the object. In this particular instance, I'm trying 
to wrap the "add" method in another named "sum" that doesn't 
modify the original object, simply by making a copy first and 
operating upon that instead.

What am I doing wrong?

The relevant bits of my class look like this:

class unitvalue{
     double coefficient;
     int[unit] exponents;

     unitvalue copy(){
         unitvalue value = new unitvalue();
         value.coefficient = this.coefficient;
         value.exponents = this.exponents.dup;
         return value;
     }

     template opmethod(string methoddef, string methodcall){
         const char[] methodtemplate =
             "unitvalue " ~ methoddef ~ "{
                 unitvalue copy = this.copy();
                 copy." ~ methodcall ~ ";
                 return copy;
             }"
         ;
     }

     mixin(unitvalue.opmethod!(
         "sum(in unitvalue value)", "add(value)"
     ));
     void add(in unitvalue value){
         if(!this.samedimensions(value)){
             throw new Exception("Cannot add values of differing 
dimensions.");
         }else{
             this.coefficient += value.coefficient;
         }
     }

     bool samedimensions(in unitvalue value){
         return this.exponents == value.exponents;
     }
}
Jan 25
next sibling parent anonymous <anonymous example.com> writes:
On 25.01.2016 15:51, pineapple wrote:
 class unitvalue{
      double coefficient;
      int[unit] exponents;

      unitvalue copy(){
          unitvalue value = new unitvalue();
          value.coefficient = this.coefficient;
          value.exponents = this.exponents.dup;
          return value;
      }

      template opmethod(string methoddef, string methodcall){
          const char[] methodtemplate =
Can't have variables like that in templates. Make it a "manifest constant" with enum. Also, if you want opmethod!("foo", "bar") to evaluate to the code, then methodtemplate needs to be renamed to "opmethod". I.e., make this line: ---- enum opmethod = ---- Also, opmethod doesn't need to be template. A function would work as well: ---- static string opmethod(string methoddef, string methodcall) { return "unitvalue " ~ methoddef ~ "{ unitvalue copy = this.copy(); copy." ~ methodcall ~ "; return copy; }" ; } mixin(unitvalue.opmethod( "sum(in unitvalue value)", "add(value)" )); ----
              "unitvalue " ~ methoddef ~ "{
                  unitvalue copy = this.copy();
                  copy." ~ methodcall ~ ";
                  return copy;
              }"
          ;
      }

      mixin(unitvalue.opmethod!(
          "sum(in unitvalue value)", "add(value)"
      ));
      void add(in unitvalue value){
          if(!this.samedimensions(value)){
              throw new Exception("Cannot add values of differing
 dimensions.");
          }else{
              this.coefficient += value.coefficient;
          }
      }

      bool samedimensions(in unitvalue value){
          return this.exponents == value.exponents;
This gives me an error, too: "Error: incompatible types for ((this.exponents) == (value.exponents)): 'int[int]' and 'const(int[int])'" (I've aliased unit to int.) As far as I can tell, this should work. There's a bugzilla issue for it: https://issues.dlang.org/show_bug.cgi?id=13622 You can work around the bogus error message by using a const temporary for this.exponent: ---- const this_exponents = this.exponents; return this_exponents == value.exponents; ----
      }
 }
Jan 25
prev sibling parent reply Chris Wright <dhasenan gmail.com> writes:
On Mon, 25 Jan 2016 14:51:21 +0000, pineapple wrote:

 I'm getting several of these since I'm trying to do the same thing in a
 few places. Here's a complete error:
 
      path\to\file.d(55): Error: variable
 units.unitvalue.opmethod!("sum(in unitvalue value)",
 "add(value)").methodtemplate cannot use template to add field to
 aggregate 'unitvalue'
      path\to\file.d(127): Error: template instance
 units.unitvalue.opmethod!("sum(in unitvalue value)", "add(value)") error
 instantiating
That looks like the right error.
      template opmethod(string methoddef, string methodcall){
          const char[] methodtemplate =
Change it from 'const' to 'enum' and Bob's your uncle. Short explanation: 'enum' refers to compile-time constants. 'const' is a promise that you will not mutate a variable through that reference. Anything that's not a compile-time constant in an aggregate body requires storage space within that aggregate, resulting in an unbounded set of fields. Exhaustive explanation: 'const' indicates that you cannot mutate the data through this reference (but it can be mutated elsewhere). It's not referring to compile-time constants. For instance, it's entirely legal (and reasonable) to write: class OptParse { const(string[]) args; this(const(string[]) args) { this.args = args; } } void main(string[] args) { new OptParse(args); } Which indicates that OptParse is not going to modify the args array. You're grabbing command-line arguments and passing them in, which means they're not a compile-time constant. So the OptParse class has to include a storage slot reserved for the 'args' variable. In your case, 'methodtemplate' isn't declared as a compile-time constant. It has an initializer that's a compile-time constant, but you aren't guaranteeing that the value isn't going to be changed. That would be fine, but it's inside a template. Templates can be instantiated a potentially unlimited number of times. So the compiler comes across this type, tries to compute its size and fields -- but it doesn't know for certain at this point how many times you're instantiating the template. So it can't list out all the fields. It would be possible if D compiled a whole program at once. This would prevent you from publishing closed-source libraries, however, and increase compilation times.
Jan 25
parent pineapple <meapineapple gmail.com> writes:
Thanks so much for the help! I'm still getting used to D's 
compile-time code.
Jan 25