Can't initialize D struct member with Range - d

I'm attempting to learn D and ran into an issue with structs and initialization. When the following code is compiled as rdmd -version=templat code.d, I get a bunch of errors like:
> dmd -version=templat code.d
D:\D\dmd2\windows\bin\..\..\src\phobos\std\random.d(1610): Error: static variable initialized cannot be read at compile time
D:\D\dmd2\windows\bin\..\..\src\phobos\std\random.d(1653): called from here: rndGen()
D:\D\dmd2\windows\bin\..\..\src\phobos\std\random.d(1653): called from here: uniform(a, b, rndGen())
code.d(8): called from here: uniform(1u, 7u)
D:\D\dmd2\windows\bin\..\..\src\phobos\std\range\package.d(3470): called from here: (*function () => uniform(1u, 7u))()
D:\D\dmd2\windows\bin\..\..\src\phobos\std\range\package.d(3387): called from here: gen.popFront()
code.d(8): called from here: generate()
code.d(13): Error: template instance `code.Dice!(1u, 7u)` error instantiating
I assume this has something to do with needing to be able to statically resolve the uniform(Lo, Hi). But I am at a loss as to how solve this issue. When I compile with rdmd -version=variabl code.d, I encounter no issues.
For what it's worth, my goal is to be able to define a "dice" type so that I can implement ranges, operator overloading, etc. on it to get a feel for this in D.
import std.range : generate ;
import std.random : uniform ;
version(templat)
{
struct Dice(uint Lo, uint Hi)
{
auto values = generate!(() => uniform(Lo, Hi));
}
void main()
{
Dice!(1, 7) d6;
}
}
version(variabl)
{
void main()
{
auto d6a = generate!(() => uniform(1, 7));
}
}

In order for generate! to work it needs to cache the first result when it's constructed, but setting a default struct values happens at compile-time. (so tries to run uniform() using ctfe...)
What you could do is use alias like this:
import std.range : take, generate;
import std.random : uniform;
import std.stdio, writeln;
alias Dice(uint Lo, uint Hi) = generate!(() => uniform(Lo, Hi));
void main()
{
auto d6 = Dice!(1, 7);
writeln(d6.front);
writeln(d6.front); // same as above because you didnt pop it
d6.popFront();
writeln(d6.front);
d6.popFront();
d6.take(3).writeln;
}
Also, here is an example of createing a range directly without useing generate!.
import std.range : take;
import std.random : uniform;
import std.stdio, writeln;
struct Dice(uint Lo, uint Hi)
{
void popFront()
{
front = uniform(Lo, Hi);
}
static bool empty = false;
uint front;
}
auto makeDice(uint Lo, uint Hi)(){
auto dice = Dice!(Lo, Hi)();
dice.popFront();
return dice;
}
void main()
{
auto d6 = makeDice!(1, 7);
writeln(d6.front);
writeln(d6.front); // same as above because you didnt pop it
d6.popFront();
writeln(d6.front);
d6.popFront();
d6.take(3).writeln;
}

Related

How to programmatically assign class/struct attributes?

I am attempting to translate my Python program to C++, but because I am new to C++ I am encountering some problems. The input file is first parsed (works, not shown) to create the INITIAL_VALUES dict/map, which I then want to use to assign the Parameters class/struct attributes using the DEST_DICT_PARAMS dict/map.
I was able to achieve this in Python code with:
import dataclasses
INITIAL_VALUES = {
"BULK": {
"MAGMA": {
"M0": 1.0,
"T0": 1320.0,
},
"ASSIM": {
"M0": 0.0,
"T0": 600.0,
},
}
}
DEST_DICT_PARAMS = {
'M0': {"MAGMA": 'Mm0', "ASSIM": 'Ma0'},
'T0': {"MAGMA": 'Tm0', "ASSIM": 'Ta0'},
}
#dataclasses.dataclass
class Parameters:
Mm0: float = None
Ma0: float = None
Ta0: float = None
Tm0: float = None
class ParametersReader:
def __init__(self):
self.parameters = Parameters()
self._assignParameters()
def _assignParameters(self):
for param_fam, dest in DEST_DICT_PARAMS.items():
for component, param in dest.items():
value = INITIAL_VALUES["BULK"][component][param_fam]
setattr(self.parameters, param, value)
params = ParametersReader()
print(params.parameters)
Output:
Parameters(Mm0=1.0, Ma0=0.0, Ta0=600.0, Tm0=1320.0)
So I wrote the corresponding C++ code:
#include <iostream>
#include <map>
using std::map;
using std::string;
map<string, map<string, map<string, float> > > INITIAL_VALUES = {{
"BULK", {
{"MAGMA", {
{"M0", 1.0},
{"T0", 1320.0},
}},
{"ASSIM", {
{"M0", 0.0},
{"T0", 600.0},
}},
}
}};
map<string, map<string, string> > DEST_DICT_PARAMS = {{
{"M0", {{"MAGMA", "Mm0"}, {"ASSIM", "Ma0"}}},
{"T0", {{"MAGMA", "Tm0"}, {"ASSIM", "Ta0"}}},
}};
struct Parameters {
float Mm0;
float Ma0;
float Ta0;
float Tm0;
} parameters;
class ParametersReader {
public:
void assignParameters_() {
map<string, map<string, string> >::iterator itr0;
map<string, string>::iterator itr1;
for (itr0 = DEST_DICT_PARAMS.begin(); itr0 != DEST_DICT_PARAMS.end(); itr0++) {
for (itr1 = itr0->second.begin(); itr1 != itr0->second.end(); itr1++) {
parameters.itr1->second = INITIAL_VALUES["BULK"][itr1->first];
}
}
}
};
int main() {
ParametersReader params;
params.assignParameters_();
}
But I'm getting an error at the line
parameters.itr1->second = INITIAL_VALUES['BULK'][itr1->first] saying "no member named 'itr1' in 'Parameters'". That error makes total sense because the code is literally trying to interpret 'itr1' as an attribute name and not the whole 'itr1->second' as the name. I think this comes down to the fact that I can't seem to find a C++ equivalent to Python's setattr(obj, name, val) function that takes an object and its attribute name and assigns it a value. Is there a C++ solution to what I am attempting?
Perhaps my entire approach is incompatible with C++. If so, would you kindly suggest an alternative approach? I would like to keep the input file format the same between the Python and C++ versions.
C++ does not have runtime reflection like Python. You cannot look up a class member by name using a runtime string because class member names do not exist at runtime.
What you can do is look up a class member via a pointer to member. This is an offset into the object calculated at compile time by the compiler:
std::map<std::string, std::map<std::string, float Parameters::*> > DEST_DICT_PARAMS = {{
{"M0", {{"MAGMA", &Parameters::Mm0}, {"ASSIM", &Parameters::Ma0}}},
{"T0", {{"MAGMA", &Parameters::Tm0}, {"ASSIM", &Parameters::Ta0}}},
}};
class ParametersReader {
public:
void assignParameters_() {
for (auto& [param_fam, dest] : DEST_DICT_PARAMS) {
for (auto& [component, param] : dest) {
parameters.*param = INITIAL_VALUES["BULK"][component][param_fam];
}
}
}
};
Demo
Note I've also used range-based for loops and structured bindings to clean up your assignParameters_ function.
C++ has no equivalent to Pythons setattr(self.parameters, param, value). If you want to have a mapping between strings and members you need to write it yourself.
You can use pointers to members to do something along the line of:
#include <map>
#include <iostream>
struct foo {
float a = 0.0f;
float b = 0.0f;
};
// list all members and their string represenation
std::map<std::string,float foo::*> mmap {
{ "a", &foo::a },
{ "b", &foo::b }
};
int main() {
foo f;
std::map<std::string,float> values {
{"a", 0.1},
{"b", 0.2}
};
for (const auto& val : values) {
// lookup the member pointers via the strings from mmap
// use the found function pointer to assing to corresponding member in f
f.*mmap[val.first] = val.second;
}
std::cout << f.a << " " << f.b << "\n";
}
Note that the code assumes that all strings present in values are also present in mmap. If not, it will fail horribly. To fix that mmap.find should be used instead and the case of not found string handleted appropriately.
This works, though there is no way to get that mapping implicitly from the class definition only. On the other, hand I can imagine libraries to exist that can help with that.

Can I implement operator overloading for D's SumType alias?

TLDR: Is there a way make D's SumType play nice with opCmp while maintaining its functionality?
Context
I'm writing a program for which D's native SumType works almost completely. However, I would like to be able to do the following:
alias Foo = SumType!(int, string);
Foo x = 3;
Foo y = 5;
writeln(max(x, y));
However, since no ordering is natively defined for SumType, I receive the following error:
C:\D\dmd2\windows\bin\..\..\src\phobos\std\algorithm\comparison.d(1644): Error: static assert: "Invalid arguments: Cannot compare types SumType!(int, string) and SumType!(int, string) for ordering."
mwe.d(11): instantiated from here: `max!(SumType!(int, string), SumType!(int, string))`
I was able to remedy this specific issue using the following method:
import std.stdio : writeln;
import std.exception : assertThrown;
import std.algorithm.comparison : max;
import core.exception : AssertError;
import std.sumtype;
struct Foo {
SumType!(int, string) value;
this(T)(T v) {
value = v;
}
ref Atom opAssign(T)(T rhs) {
value = rhs;
return this;
}
int opCmp(Foo other) {
return match!(
(a, b) => a < b ? -1 : a == b ? 0 : 1,
(_1, _2) => assert(0, "Cannot match")
)(value, other.value);
}
}
void main() {
Foo x = 3;
Foo y = 7;
Foo z = "asdf";
assert(x < y); // comparing ints works correctly
assertThrown!AssertError(x < z); // cannot compare int and string
assert(max(x, y) == y); // D's max works
}
The Problem
While I can now use x.value.match!(...) where I used to use x.match!(...), I would like to still be able to call .match! directly on x, and also use match!(...)(x, y) instead of match!(...)(x.value, y.value). I do not like the idea of inserting hundreds of .value throughout my code just to make certain functions like max work, and would prefer if there were a more elegant solution. I tried tinkering around with defining a custom opDispatch using mixins but I couldn't get that to play nicely with the existing SumType:
struct Foo {
SumType!(int, string) value;
this(T)(T v) {
value = v;
}
ref Atom opAssign(T)(T rhs) {
value = rhs;
return this;
}
int opCmp(Foo other) {
return match!(
(a, b) => a < b ? -1 : a == b ? 0 : 1,
(_1, _2) => assert(0, "Cannot match")
)(value, other.value);
}
auto opDispatch(string name, T...)(T vals) {
return mixin("value." ~ name)(vals);
}
}
void main() {
Foo y = 7;
y.match!(
(int intValue) => writeln("Received an integer"),
(string strValue) => writeln("Received a string")
);
}
And I am unable to decode the error which results:
mwe.d(38): Error: none of the overloads of template `std.sumtype.match!(function (int intValue) #safe
{
writeln("Received an integer");
return ;
}
, function (string strValue) #safe
{
writeln("Received a string");
return ;
}
).match` are callable using argument types `!()(Foo)`
C:\D\dmd2\windows\bin\..\..\src\phobos\std\sumtype.d(1659): Candidate is: `match(SumTypes...)(auto ref SumTypes args)`
with `SumTypes = (Foo)`
must satisfy the following constraint:
` allSatisfy!(isSumType, SumTypes)`
Beyond that I am out of ideas as to how to find a less clunky solution.
I suggest giving alias this a try. Similar to class inheritance, this lets you specialize a type and let other things fall back to the original member.
import std.stdio : writeln;
import std.exception : assertThrown;
import std.algorithm.comparison : max;
import core.exception : AssertError;
import std.sumtype;
struct Foo {
SumType!(int, string) value;
this(T)(T v) {
value = v;
}
int opCmp(Foo other) {
return match!(
(a, b) => a < b ? -1 : a == b ? 0 : 1,
(_1, _2) => assert(0, "Cannot match")
)(value, other.value);
}
alias value this;
}
void main() {
Foo x = 3;
Foo y = 7;
Foo z = "asdf";
assert(x < y); // comparing ints works correctly
assertThrown!AssertError(x < z); // cannot compare int and string
assert(max(x, y) == y); // D's max works
// this will now automatically fall back to y.value.match
y.match!(
(int intValue) => writeln("Received an integer"),
(string strValue) => writeln("Received a string")
);
}
See, you still must construct your special type, but then after that, it will look up there for members. It will find the opCmp, letting it extend the type. But then for everything else, since it isn't there, it will try checking obj.value instead, falling back to the original type.
This doesn't always work, and it means it will implicitly convert too, meaning you can pass a Foo to a void thing(SumType!(int, string)) with it passing foo.value to the function, which may or may not be desirable.
But I think it is the closest thing to what you want here.
(note btw why you got an error originally is that match isn't actually a member of SumType. it is an outside free function that takes all the match lambdas as template arguments. An opDispatch could forward template arguments too - it can be done in a two-level definition - but since match is not a member anyway, it isn't quite going to solve things anyway whereas the alias this does seem to work)

Adding a custom type to a RedBlackTree

I want to keep an ordered set of records and the standard provides me with RedBlackTree. The record is of type Tuple!(string, uint). Here's what it looks like:
import std.json : parseJSON;
uint[string] wordTable;
import std.datetime.stopwatch : StopWatch, AutoStart;
auto sw = StopWatch(AutoStart.yes);
const auto j = parseJSON(get(link));
const long downloadTime = sw.peek.total!"msecs";
import std.typecons : Tuple, tuple;
import std.container.rbtree : RedBlackTree;
import std.functional : binaryFun;
RedBlackTree!(Tuple!(string, uint), binaryFun!("a[1] > b[1]")) records;
foreach (node; j["posts"].array()) {
import std.stdio : writeln;
import std.utf : decode;
if ("com" in node) {
import std.algorithm : splitter;
foreach (word; getStr(node["com"].str()).splitter(' ')) {
import std.string : strip;
if (word.strip().length > 0)
wordTable.require(word, 0)++;
records ~= (tuple(word, wordTable[word])); // error
}
}
}
Now primarily I had used insert() method to add a record to the records but it causes segfault in runtime. So I decided to use ~= in hopes for better error messages. And here's what the compiler says:
Error: cannot append type Tuple!(string, uint) to type std.container.rbtree.RedBlackTree!(Tuple!(string, uint), binaryFun, false)
According to https://dlang.org/phobos/std_container_rbtree.html#.RedBlackTree I have to provide a type such that calling less(a, b) on it returns a boolean. So I went ahead and created a type for it:
struct Record {
string key;
uint value;
int opCmp(ref const Record other) const {
return value - other.value;
}
}
// bool less(Record a, Record b) {
// return a < b;
// }
void main(string[] args) {
import std.stdio : writeln, writefln;
if (args.length < 3) {
writeln("Must have 2 arguments " ~ "first argument is the link, "
~ "the second one is for minimum repeatation threshold. Exiting.");
import core.stdc.stdlib : exit;
exit(-1);
}
const auto link = parseLink(args[1]);
const auto threshold = atoui(args[2]);
import std.json : parseJSON;
uint[string] wordTable;
import std.datetime.stopwatch : StopWatch, AutoStart;
auto sw = StopWatch(AutoStart.yes);
const auto j = parseJSON(get(link));
const long downloadTime = sw.peek.total!"msecs";
import std.container.rbtree : RedBlackTree;
import std.functional : binaryFun;
RedBlackTree!Record records;
foreach (node; j["posts"].array()) {
import std.utf : decode;
if ("com" in node) {
import std.algorithm : splitter;
foreach (word; getStr(node["com"].str()).splitter(' ')) {
import std.string : strip;
if (word.strip().length > 0)
wordTable.require(word, 0)++;
records ~= (Record(word, wordTable[word]));
}
}
}
This time the compiler complains:
Error: cannot append type Record to type std.container.rbtree.RedBlackTree!(Record, "a < b", false)
So the gist of the question is, if I have an RedBlackTree with a custom binaryFun, how can I add an instance of a tuple or a custom type to it?

How do I call PBNI methods from Powerbuilder, using method ID

I am creating a c++ PBNI non-visual extension, calling it from powerbuilder like this :
textbox1.text = string (cpp_add.f_add(integer(textbox2.text), integer(textbox3.text)))
PBX_GetDescription() has one class and two functions described this way:
"class pbadd from nonvisualobject \n" \
"function int f_add(int a, int b)\n" \
"function int f_add2(int a, int b)\n" \
"end class \n"
Header:
#include "pbext.h"
class pbadd : public IPBX_NonVisualObject
{
public:
pbadd();
virtual ~pbadd();
PBXRESULT Invoke(
IPB_Session *session,
pbobject obj,
pbmethodID mid,
PBCallInfo *ci);
int f_add(IPB_Session*, pbint, pbint);
int f_add2(IPB_Session*, pbint, pbint);
enum MethodIDs
{
mAdd = 1,
mAdd2 = 2
};
private:
virtual void Destroy();
};
And the required Invoke method:
PBXRESULT pbadd::Invoke(IPB_Session *Session,
pbobject obj, pbmethodID mid, PBCallInfo *ci)
{
if (mid == mAdd)
{
int sum = f_add(Session, ci->pArgs->GetAt(0)->
GetInt(), ci->pArgs->GetAt(1)->GetInt());
ci->returnValue->SetInt(sum);
}
if (mid == mAdd2)
{
int sum = f_add2(Session, ci->pArgs->GetAt(0)->
GetInt(), ci->pArgs->GetAt(1)->GetInt());
ci->returnValue->SetInt(sum+1);
}
return PBX_OK;
}
Now the problem is: I have no clue how to call second of the two methods. I am assuming I can somehow change the pbmethod mid, but I do not know how to do so from powerbuilder.
Did you in PowerBuilder right click the library and choose 'Import PB Extension'? Doing that creates a 'shell' object that you use in your PB script and it automatically calls the Invoke method passing the method id.
Apparently the problem was here:
enum MethodIDs
{
mAdd = 1,
mAdd2 = 2
};
The IDs are counted from 0. Correct:
enum MethodIDs
{
mAdd = 0,
mAdd2 = 1
};

Get the variable values at runtime using reflection in Dlang

Is it possible to get the class/struct/other variables value during runtime in dlang to get/set its value? If yes how to do that please provide example.
And also is it possible to get the runtime variable value?
Ex:
class S{ int svariable = 5;}
class B { int bvariable = 10;}
void printValue(T, T instanceVariable, string variableName) {
writeln("Value of ", variableName, "=", instanceVariable.variableName);
}
Output:
Value of svariable = 5;
Value of bvariable = 10;
There is a library named witchcraft that allows for runtime reflection. There are examples of how to use it on that page.
I'd first recommend trying a reflection library like #mitch_ mentioned. However, if you want to do without an external library, you can use getMember to get and set fields as well as invoke functions:
struct S {
int i;
int fun(int val) { return val * 2; }
}
unittest {
S s;
__traits(getMember, s, "i") = 5; // set a field
assert(__traits(getMember, s, "i") == 5); // get a field
assert(__traits(getMember, s, "fun")(12) == 24); // call a method
}