(Note that I am extremely new to DLang (first day) so I am probably doing something very stupid)
I am trying to create a mixin template to re-use in my app's domain classes, which will automatically generate a toString1() method at compile-time, which will loop through the implementing struct's properties and generate code to print them.
To accomplish this, I am getting a const array of all of the struct's properties and trying to loop through them. However, when I try to access the index inside the loop (which should be perfectly executable at compile-time -- since we are looping against a known value) it fails.
It works completely fine if I use hard-coded indexes.
template Printable() {
import std.algorithm.iteration;
import std.conv;
import std.meta;
import std.stdio;
import std.format;
import std.range;
import std.typecons;
string toString1() const {
const auto traits = __traits(allMembers, typeof(this));
const auto internalWrap = (const string s) => "`" ~ s ~ ": ` ~ this." ~ s ~ " ~ `\n`";
const auto ret = iota(0, traits.length)
.map!((const int x) => traits[x]) // Error: variable x cannot be read at compile time
.fold((a, b) => a ~ b);
pragma(msg, internalWrap(traits[0])); // WORKS GREAT
return "";
}
}
(Just a note that I also tried doing it using both for-loops as well, but it fails with the same error).
I wanted to put an answer on this so this is a code example about what we went over in the comments:
struct A {
int a;
string b;
mixin Printable; // adds the toString1 method
}
mixin template Printable() {
string toString1() const {
string s;
import std.conv;
// loop over the trait directly, don't try to feed it
// through variables to ensure still available
foreach(memberName; __traits(allMembers, typeof(this))) {
// this ensures we are only actually trying to print actual fields
// (only fields have an offsetof property) since printing member
// variables will not be helpful
static if(is(typeof(__traits(getMember, this, memberName).offsetof))) {
if(s.length)
s ~= "\n";
// name
s ~= memberName;
s ~= " = ";
// value, converted to string
s ~= to!string(__traits(getMember, this, memberName));
}
}
return s;
}
}
void main() {
import std.stdio;
A a = A(14, "string");
writeln(a.toString1());
}
prints
a = 14
b = string
Related
For example, consider the following C# code:
interface IBase { void f(int); }
interface IDerived : IBase { /* inherits f from IBase */ }
...
void SomeFunction()
{
IDerived o = ...;
o.f(5);
}
I know how to get a MethodDefinition object corresponding to SomeFunction.
I can then loop through MethodDefinition.Instructions:
var methodDef = GetMethodDefinitionOfSomeFunction();
foreach (var instruction in methodDef.Body.Instructions)
{
switch (instruction.Operand)
{
case MethodReference mr:
...
break;
}
yield return memberRef;
}
And this way I can find out that the method SomeFunction calls the function IBase.f
Now I would like to know the declared type of the object on which the function f is called, i.e. the declared type of o.
Inspecting mr.DeclaringType does not help, because it returns IBase.
This is what I have so far:
TypeReference typeRef = null;
if (instruction.OpCode == OpCodes.Callvirt)
{
// Identify the type of the object on which the call is being made.
var objInstruction = instruction;
if (instruction.Previous.OpCode == OpCodes.Tail)
{
objInstruction = instruction.Previous;
}
for (int i = mr.Parameters.Count; i >= 0; --i)
{
objInstruction = objInstruction.Previous;
}
if (objInstruction.OpCode == OpCodes.Ldloc_0 ||
objInstruction.OpCode == OpCodes.Ldloc_1 ||
objInstruction.OpCode == OpCodes.Ldloc_2 ||
objInstruction.OpCode == OpCodes.Ldloc_3)
{
var localIndex = objInstruction.OpCode.Op2 - OpCodes.Ldloc_0.Op2;
typeRef = locals[localIndex].VariableType;
}
else
{
switch (objInstruction.Operand)
{
case FieldDefinition fd:
typeRef = fd.DeclaringType;
break;
case VariableDefinition vd:
typeRef = vd.VariableType;
break;
}
}
}
where locals is methodDef.Body.Variables
But this is, of course, not enough, because the arguments to a function can be calls to other functions, like in f(g("hello")). It looks like the case above where I inspect previous instructions must repeat the actions of the virtual machine when it actually executes the code. I do not execute it, of course, but I need to recognize function calls and replace them and their arguments with their respective returns (even if placeholders). It looks like a major pain.
Is there a simpler way? Maybe there is something built-in already?
I am not aware of an easy way to achieve this.
The "easiest" way I can think of is to walk the stack and find where the reference used as the target of the call is pushed.
Basically, starting from the call instruction go back one instruction at a time taking into account how each one affects the stack; this way you can find the exact instruction that pushes the reference used as the target of the call (a long time ago I wrote something like that; you can use the code at https://github.com/lytico/db4o/blob/master/db4o.net/Db4oTool/Db4oTool/Core/StackAnalyzer.cs as inspiration).
You'll need also to consider scenarios in which the pushed reference is produced through a method/property; for example, SomeFunction().f(5). In this case you may need to evaluate that method to find out the actual type returned.
Keep in mind that you'll need to handle a lot of different cases; for example, imagine the code bellow:
class Utils
{
public static T Instantiate<T>() where T : new() => new T();
}
class SomeType
{
public void F(int i) {}
}
class Usage
{
static void Main()
{
var o = Utils.Instantiate<SomeType>();
o.F(1);
}
}
while walking the stack you'll find that o is the target of the method call; then you'll evaluate Instantiate<T>() method and will find that it returns new T() and knowing that T is SomeType in this case, that is the type you're looking for.
So the answer of Vagaus helped me come up with a working implementation.
I published it on github - https://github.com/MarkKharitonov/MonoCecilExtensions
Included many unit tests, but I am sure I missed some cases.
I am fairly new to Stan and have gone through the manual (version 2.23). What was new to me was that variable hiding is not allowed: you can not use a local variable (e.g. in a for-loop) that has been defined globally (i.e. outside the for-loop) (chapter 7.9, Local Variable Declarations).
Is the same true for user-defined functions? I.e., can you declare variables in user-defined functions that have the same name as other variables, that have been declared outside the function? In our case, we have
functions{
real[] my_function (x) {
real init[K*2] = some_declaration_involving_x
return(some_other_value_involving_init[])
}
}
transformed data {
real init[K*6] = some_other_declaration; // initial values
}
transformed parameters {
yet_another_variable = my_function(some_variable)
}
Yes, it's easy enough to test:
functions {
int fun() {
int N = 1;
return N;
}
}
model {
real N = 2;
print(fun(), N);
}
I have a script which exports excel worksheets to csv, and I am attempting to escape commas, quotes and the like. I have created a static method that encodes the value of the cell being accessed. This method references a static variable which stores a Pattern.compile() value.
I have tried using def rxquote within the method but this gives me a different error stating that using static modifier before declaring my rxquote variable is illegal. Code is below followed by error message.
#!/usr/bin/env groovy
#Grab(group = 'org.apache.poi', module = 'poi', version = '4.1.0')
#Grab(group = 'org.apache.poi', module = 'poi-ooxml', version = '4.1.0')
import java.util.regex.*
import org.apache.poi.xssf.usermodel.XSSFWorkbook
import org.apache.poi.ss.usermodel.*
static Pattern rxquote = Pattern.compile("\"")
static private String encodeValue(String value) {
boolean needQuotes = false;
if ( value.indexOf(',') != -1 || value.indexOf('"') != -1 ||
value.indexOf('\n') != -1 || value.indexOf('\r') != -1 ){
needQuotes = true;
}
Matcher m = rxquote.matcher(value)
if ( m.find() ) {
needQuotes = true
value = m.replaceAll("\"\"")
}
if ( needQuotes ) {
return "\"" + value + "\""
}
else return value;
}
//for(){
// ... export csv code (which works on its own)
//}
Error message on compile:
Apparent variable 'rxquote' was found in a static scope but doesn't refer to a local variable, static field or class. Possible causes:
You attempted to reference a variable in the binding or an instance variable from a static context.
You misspelled a classname or statically imported field. Please check the spelling.
You attempted to use a method 'rxquote' but left out brackets in a place not allowed by the grammar.
# line 27, column 17.
Matcher m = rxquote.matcher(value);
^
I've tried researching the issue and have found several similar questions here, but none of the solutions appear to apply to this situation as far as I can tell. I expected a static declaration of the variable to avoid this problem, but it seems there's something I'm missing.
you can't declare static variable in groovy script.
it's allowed only in groovy/java class.
error does not correspond to situation.
should be : Modifier 'static' not allowed here.
as workaround for static variables you can use some class:
class Const{
static String bar = 'test'
}
static private String foo() {
return Const.bar
}
foo()
I'm trying to multiply some string a by some integer b such that a * b = a + a + a... (b times). I've tried doing it the same way I would in python:
class Test {
static function main() {
var a = "Text";
var b = 4;
trace(a * b); //Assumed Output: TextTextTextText
}
}
But this raises:
Build failure Test.hx:6: characters 14-15 : String should be Int
There doesn't seem to be any information in the Haxe Programming Cookbook or the API Documentation about multiplying strings, so I'm wondering if I've mistyped something or if I should use:
class Test {
static function main() {
var a = "Text";
var b = 4;
var c = "";
for (i in 0...b) {
c = c + a;
}
trace(c); // Outputs "TextTextTextText"
}
}
Not very short, but array comprehension might help in some situations :
class Test {
static function main() {
var a = "Text";
var b = 4;
trace( [for (i in 0...b) a].join("") );
//Output: TextTextTextText
}
}
See on try.haxe.org.
The numeric multiplication operator * requires numeric types, like integer. You have a string. If you want to multiply a string, you have to do it manually by appending a target string within the loop.
The + operator is not the numeric plus in your example, but a way to combine strings.
You can achieve what you want by operator overloading:
abstract MyAbstract(String) {
public inline function new(s:String) {
this = s;
}
#:op(A * B)
public function repeat(rhs:Int):MyAbstract {
var s:StringBuf = new StringBuf();
for (i in 0...rhs)
s.add(this);
return new MyAbstract(s.toString());
}
}
class Main {
static public function main() {
var a = new MyAbstract("foo");
trace(a * 3); // foofoofoo
}
}
To build on tokiop's answer, you could also define a times function, and then use it as a static extension.
using Test.Extensions;
class Test {
static function main() {
trace ("Text".times(4));
}
}
class Extensions {
public static function times (str:String, n:Int) {
return [for (i in 0...n) str].join("");
}
}
try.haxe.org demo here
To build on bsinky answer, you can also define a times function as static extension, but avoid the array:
using Test.Extensions;
class Test {
static function main() {
trace ("Text".times(4));
}
}
class Extensions {
public static function times (str:String, n:Int) {
var v = new StringBuf();
for (i in 0...n) v.add(str);
return v.toString();
}
}
Demo: https://try.haxe.org/#e5937
StringBuf may be optimized for different targets. For example, on JavaScript target it is compiled as if you were just using strings https://api.haxe.org/StringBuf.html
The fastest method (at least on the JavaScript target from https://try.haxe.org/#195A8) seems to be using StringTools._pad.
public static inline function stringProduct ( s : String, n : Int ) {
if ( n < 0 ) {
throw ( 1 );
}
return StringTools.lpad ( "", s, s.length * n );
}
StringTools.lpad and StringTools.rpad can't seem to decide which is more efficient. It looks like rpad might be better for larger strings and lpad might be better for smaller strings, but they switch around a bit with each rerun. haxe.format.JsonPrinter uses lpad for concatenation, but I'm not sure which to recommend.
I was wondering how I could change the code below such the bmBc is computed at compile time . The one below works for runtime but it is not ideal since I need to know the bmBc table at compile-time . I could appreciate advice on how I could improve on this.
import std.conv:to;
import std.stdio;
int [string] bmBc;
immutable string pattern = "GCAGAGAG";
const int size = to!int(pattern.length);
struct king {
void calculatebmBc(int i)()
{
static if ( i < size -1 )
bmBc[to!string(pattern[i])]=to!int(size-i-1);
// bmBc[pattern[i]] ~= i-1;
calculatebmBc!(i+1)();
}
void calculatebmBc(int i: size-1)() {
}
}
void main(){
king myKing;
const int start = 0;
myKing.calculatebmBc!(start)();
//1. enum bmBcTable = bmBc;
}
The variables bmBc and bmh can't be read at compile time because you define them as regular runtime variables.
You need to define them as enums, or possibly immutable, to read them at compile time, but that also means that you cannot modify them after initialization. You need to refactor your code to return values instead of using out parameters.
Alternatively, you can initialize them at runtime inside of a module constructor.