Jump to page: 1 2
Thread overview
Messing with betterC and string type.
Sep 06, 2018
SrMordred
Sep 06, 2018
Adam D. Ruppe
Sep 06, 2018
SrMordred
Sep 06, 2018
Adam D. Ruppe
Sep 07, 2018
Kagamin
Sep 07, 2018
Trass3r
Sep 07, 2018
SrMordred
Sep 07, 2018
Adam D. Ruppe
Sep 07, 2018
H. S. Teoh
Sep 07, 2018
SrMordred
Sep 07, 2018
Meta
Sep 07, 2018
Jonathan M Davis
Sep 07, 2018
Adam D. Ruppe
September 06, 2018
I'm most of the time exploring the betterC lands
and was thinking about custom strings, and how to convert D into D-betterC

hmm I wonder...

struct String{}
alias string = String;

string x = "test";

//cannot implicitly convert expression "test" of type string to String

ok then...

struct String{
    this(string x){}
}
//constructor app.String.this(String x) is not callable using argument types (string)

Ok now things got weird.

...
this(String x){}
//same error.

Ok the string is a String but not a string... what monster I created?

How to tame this chimera? templates off course
this(T)(T t){}
//compiles!!

Now some true utility:
string x = "test";
x = x ~ x;
//incompatible types for (x) ~ (x): both operands are of type String

as expected:
...
String opBinary(string op, T)(T other)
{
    return String();
}
//incompatible types for (x) ~ (x): both operands are of type String
hmmm, whats happening?

x.opBinary!"~"(x);

//template app.String.opBinary cannot deduce function from argument types !("~")(String), candidates are:
//source\app.d(9,12):        app.String.opBinary(String op, T)(T other)

Oh, it must be the string is not string chaos that I started.

String opBinary(alias op, T)(T other)
{
    return String();
}
//compiles!!

Ok, so then *maybe* I can transform D normal code using 'string' to D-BetterC only with aliasing my custom struct.(off course, besides all other problems that may emerge)

My question is, i´m breaking something else, or this could be a valid approach?






September 06, 2018
On Thursday, 6 September 2018 at 16:24:12 UTC, SrMordred wrote:
> alias string = String;

For the rest of this module, any time you write `string`, the compiler sees `String`. But inside the compiler, it still thinks of its own string, hence the confusing looking error messages.

> struct String{
>     this(string x){}
> }

And this is going to be seen as

this(String x) {}

instead of what you wanted. Try

this(object.string x) {}

which MIGHT work, or

this(immutable(char)[] x) {}

which will work - immutable(char)[] is what object.string actually is (and the compiler will often use that - immutable(char)[], the proper name - and string, the user-friendly name, totally interchangably).


> My question is, i´m breaking something else, or this could be a valid approach?

It is valid as long as you keep the names straight. Though it won't be as cool as you think because D doesn't do implicit construction, so

void foo(string s) {}

foo("this");

won't compile, since it won't make a String out of that immutable(char)[] literal without an explicit initialization of some sort.

You could, of course, just do a wrapper function or whatever, but really I think you are better off just trying to work with immutable(char)[]...
September 06, 2018
On Thursday, 6 September 2018 at 16:50:01 UTC, Adam D. Ruppe wrote:
> this(object.string x) {}
Yep, this works.

> which will work - immutable(char)[] is what object.string actually is (and the compiler will often use that - immutable(char)[], the proper name - and string, the user-friendly name, totally interchangably).

Yes, the true problem arrives on the operations like concat "~" that call some internal function to do that with strings. I can hijack the string identifier, but i can´t replace the concat operator right?
(Well, i already tried the module object; trick, but didn´t go much far with that path.)

> void foo(string s) {}
>
> foo("this");
>
> won't compile, since it won't make a String out of that immutable(char)[] literal without an explicit initialization of some sort.
>
//cannot pass argument "this" of type string to parameter String s
iep, this seems a real problem. ;/
September 06, 2018
On Thursday, 6 September 2018 at 17:09:34 UTC, SrMordred wrote:
> Yes, the true problem arrives on the operations like concat "~" that call some internal function to do that with strings.

Only if it is string ~ string. If it is your type, that's where opBinary and opBinaryRight come in.

YourString ~ built_in_string = YourString.opBinary(string op : "~")(immutable(char)[] rhs);

built_in_string ~ YourString = YourString.opBinaryRight(string op : "~")(immutable(char)[] lhs);


so you can make it work.


Though btw I would actually suggest leaving concat unimplemented... it is so hard to manage the memory for it without the GC. IMO better off just appending to an existing thing; do ~= instead of ~.

> iep, this seems a real problem. ;/

yeah we have no implict ctors :(
September 07, 2018
On Thursday, 6 September 2018 at 17:09:34 UTC, SrMordred wrote:
>> void foo(string s) {}
>>
>> foo("this");
>>
>> won't compile, since it won't make a String out of that immutable(char)[] literal without an explicit initialization of some sort.
>>
> //cannot pass argument "this" of type string to parameter String s
> iep, this seems a real problem. ;/

You can sort of have custom literals like in C++

String s(object.string t){ return String(t); }

foo("this".s);
September 07, 2018
On Friday, 7 September 2018 at 08:26:11 UTC, Kagamin wrote:
> You can sort of have custom literals like in C++
>
> String s(object.string t){ return String(t); }
>
> foo("this".s);

Awesome :D
September 07, 2018
On Thursday, September 6, 2018 11:09:34 AM MDT SrMordred via Digitalmars-d wrote:
> > void foo(string s) {}
> >
> > foo("this");
> >
> > won't compile, since it won't make a String out of that immutable(char)[] literal without an explicit initialization of some sort.
>
> //cannot pass argument "this" of type string to parameter String s iep, this seems a real problem. ;/

It's one of those decisions that was made to prevent certain classes of bugs - which it does do - but then it gets very annoying in situations like this. Whether it's better to have implicit construction and risk the bugs that come with it or whether it's better to avoid the bugs and thus not have implicit construction is debatable.

What probably makes it particularly bad for C++ though is that C++ will (IIRC) do up to three conversions when calling a function. So, depending on what types you have available for a piece of code, you could end up with nonesense like

void foo(String S) { ... }

foo(42);

compiling (e.g. if there were a type that could be explicitly constructed from int, and it in turn implicitly converted to String - or if that type implicitly converted to a const char*, and String could be implicitly constructed from it). One of the tactics to reduce the problems in C++ is to make constructors explicit as much as possible to reduce the number of possible conversions, but if you have enough implicit constructions and/or conversions in place, it can get pretty nasty.

If we had implicit construction in D but only allowed one level of conversion (e.g. String could be implicitly constructed from a const(char)[], but a type that implicitly converted to const(char)[] couldn't be used to construct a String), maybe it would be sane enough. I don't know. This is one of those situations where doing what many folks would consider to be more user-friendly can quickly introduce subtle and annoying bugs - hence why D was conservative about it. And honestly, the implicit conversions that we already have create enough problems with stuff like templates as it is without adding in stuff like implicit construction. If only we could somehow have all of the useful implicit conversion/construction stuff without actually getting subtle bugs out of the deal... :|

In any case, all you have to do is just be explicit about construction, which an be somewhat annoying but for the most part isn't a big deal. And as Kagamin pointed out, you can always create a helper function with a really short name to call the constructor if you want to.

It wouldn't surprise me if someone writes a DIP on implicit construction at some point, but at least for now, for better or worse, we get to live without it.

- Jonathan M Davis



September 07, 2018
On Friday, 7 September 2018 at 13:58:41 UTC, Jonathan M Davis wrote:
> If we had implicit construction in D but only allowed one level of conversion (e.g. String could be implicitly constructed from a const(char)[], but a type that implicitly converted to const(char)[] couldn't be used to construct a String), maybe it would be sane enough.

Perhaps. I'd actually be kinda ok (just kinda tho) even if it only allowed implicit construction from built-in literals, and only if it is explicitly marked as an implicit ctor.

The main use cases I'd care about are:

void foo(LibraryAAType x) {}
foo(null); // impossible right now, but works with built-ins

void bar(LibraryStringType x) {}
bar(null);
bar("whatever");


And similar. Actually constructing from a variable isn't that important:

string s = "";
bar(s); // I can live with this being an error


BUT, that said, if we can have it, I'd still appreciate it:

string s = "";
void baz(Variant b) {}

baz(s); // would be nice!



Just, indeed:

struct S {
   string s;
   alias s this;
}

struct Y {
  @implicit this(string s) {}
}

void foo(Y y) {}

S s;
foo(s);

Should that compile? ...eeeh, I can see the argument for yes, I actually think the implementation will be easier to make it work, since it follows pretty simple rules (foo(s) didn't work, automatically try foo(s.s), find it does work, carry on), but I am totally - totally - ok with saying no to that.


But, what is an absolute must for me: implicit construction MUST be opt in on the constructor. THAT is the mistake C++ made in my eyes: they made `explicit` the keyword to turn it off, when they should have made `implicit` the keyword to turn it on.

> And as Kagamin pointed out, you can always create a helper function with a really short name to call the constructor if you want to.

you can also do it with a template:

s!"my new string"

which has a few potential optimization benefits, especially if it needs some kind of processing. (though I would also note it can be a pessimization cuz the compiler will build the string into symbol names and not discard them...)

But for replacing a built in, it is still nice if `null` actually works, and implicit construction is kinda a must there. (Of course, that's the reasoning that led C++ down its path too - making std::string work with char*...)
September 07, 2018
On Friday, 7 September 2018 at 08:26:11 UTC, Kagamin wrote:
> On Thursday, 6 September 2018 at 17:09:34 UTC, SrMordred wrote:
>>> void foo(string s) {}
>>>
>>> foo("this");
>>>
>>> won't compile, since it won't make a String out of that immutable(char)[] literal without an explicit initialization of some sort.
>>>
>> //cannot pass argument "this" of type string to parameter String s
>> iep, this seems a real problem. ;/
>
> You can sort of have custom literals like in C++
>
> String s(object.string t){ return String(t); }
>
> foo("this".s);

Yes, but you don't really need this function.

struct String
{
    this(object.string s){}
}
foo("this".String);
alias s = String; //if you want.
foo("this".s);

September 07, 2018
On Friday, 7 September 2018 at 15:48:39 UTC, SrMordred wrote:
> Yes, but you don't really need this function.

Whoa, when was that added?! I don't remember ctors via ufcs being there but indeed, it works on newest dmd.
« First   ‹ Prev
1 2