January 17, 2019
On Tuesday, 15 January 2019 at 11:14:54 UTC, John Burton wrote:
> As an example let's say I have a type 'Window' that represents a win32 window. I'd like to be able to construct an instance of the type with some optional parameters that default to some reasonable settings and create the underlying win32 window.
>
> I'd ideally like some syntax like this :-
>
> auto window = Window(title = "My Window", width = 1000, fullscreen = true);
>
> Assume that title, width, fullscreen are optional and if not specified there are defaults to use. And that there are many other settings than just these 3 that I've chosen to just use the default here.
>
> I know that I can't do it like this is D but what is the best way to achieve this kind of thing? I can add properties and then do a specific "create" function to create the underlying win32 window once I'm done but that seems ugly.
>
> auto window = Window();
> window.title = "My Window";
> window.width = 1000;
> window.create();
>
> This is ok, but I'm not so keen on separating the creation and construction like this.
> Is there a better way that's not ugly?

Let me throw this idea here:


struct Config
{
	string title;
	int width;
}

struct Window
{
	this(Config config)
	{
		//use static foreach magic to set everything :P
	}
}

auto NewWindow( alias code )()
{
	mixin("Config config = {"~code~"};");
	return Window(config);
}

//usage:
auto a = NewWindow!q{ title : "MainTitle" };
auto b = NewWindow!q{ title : "MainTitle", width : 800 };
auto c = NewWindow!q{ width : 1000 };
auto d = NewWindow!q{};


:)
January 17, 2019
On Thursday, 17 January 2019 at 01:43:42 UTC, SrMordred wrote:
> On Tuesday, 15 January 2019 at 11:14:54 UTC, John Burton wrote:
>> [...]
>
> Let me throw this idea here:
>
>
> struct Config
> {
> 	string title;
> 	int width;
> }
>
> struct Window
> {
> 	this(Config config)
> 	{
> 		//use static foreach magic to set everything :P
> 	}
> }
>
> auto NewWindow( alias code )()
> {
> 	mixin("Config config = {"~code~"};");
> 	return Window(config);
> }
>
> //usage:
> auto a = NewWindow!q{ title : "MainTitle" };
> auto b = NewWindow!q{ title : "MainTitle", width : 800 };
> auto c = NewWindow!q{ width : 1000 };
> auto d = NewWindow!q{};
>
>
> :)

Oh that's interesting!
January 17, 2019
On Wednesday, 16 January 2019 at 14:59:01 UTC, Kagamin wrote:> On Tuesday, 15 January 2019 at 11:14:54 UTC, John Burton wrote:
>> auto window = Window(title = "My Window", width = 1000, fullscreen = true);
>
> In this particular case I would make the constructor take 3 parameters - title, width and height. Full screen is a rare functionality and shouldn't clutter the constructor, can it be set after the window is created?

Well window was just an example really, my real use case is a similar
object that needs a lot of configuration where mostly the default works
but you might want to override, and the config is needed to create the
object in the first place.

For this example you are right though, and it may be that I'm overthinking
the whole thing.
January 17, 2019
On Thu, Jan 17, 2019 at 10:29:13AM +0000, John Burton via Digitalmars-d-learn wrote: [...]
> Well window was just an example really, my real use case is a similar object that needs a lot of configuration where mostly the default works but you might want to override, and the config is needed to create the object in the first place.
[...]

When I encounter similar situations in my code, my go-to solution is to use a struct with default field values as a configuration object that you pass to the ctor. You can either pass .init to the ctor to get default behavior, or declare an instance of the struct and customize as you see fit before handing it to the ctor.


--T
January 17, 2019
On Thursday, 17 January 2019 at 01:43:42 UTC, SrMordred wrote:
> Let me throw this idea here:
>...

I usually do this too, I like to use struct and then in another language I use reflection do optimize binding.

Anyway I understood all your code, except for this "alias code"

> auto NewWindow( alias code )()
> {
> 	mixin("Config config = {"~code~"};");
> 	return Window(config);
> }

Looking on specs: https://dlang.org/spec/declaration.html#alias

"AliasDeclarations create a symbol that is an alias for another type, and can be used anywhere that other type may appear."

So with your example imagine this:

foo(alias x){}

foo("a");
foo(1);

'x' will be string one time and integer another? Or there is something that I'm missing.

Matheus.
January 17, 2019
On Thursday, 17 January 2019 at 12:11:02 UTC, Matheus wrote:
>
> foo(alias x){}
>
> foo("a");
> foo(1);
>
> 'x' will be string one time and integer another? Or there is something that I'm missing.
>
> Matheus.

Yes, but there is a mistake there:
alias is part of the template:

foo(alias x)(){} //note extra parens

than u call like an template:

foo!"a"; //equivalent = foo!("a")();
foo!1;
January 17, 2019
On Thursday, 17 January 2019 at 01:43:42 UTC, SrMordred wrote:
> On Tuesday, 15 January 2019 at 11:14:54 UTC, John Burton wrote:

[...]

>> auto window = Window();
>> window.title = "My Window";
>> window.width = 1000;
>> window.create();

[...]

>> Is there a better way that's not ugly?

[...]

> //usage:
> auto a = NewWindow!q{ title : "MainTitle" };
> auto b = NewWindow!q{ title : "MainTitle", width : 800 };
> auto c = NewWindow!q{ width : 1000 };
> auto d = NewWindow!q{};
>
>
> :)

Put a semicolon instead of the comma in the line "auto b ..." and compile.
January 17, 2019
On Thursday, 17 January 2019 at 16:55:33 UTC, SrMordred wrote:
> Yes, but there is a mistake there:
> alias is part of the template:
>
> foo(alias x)(){} //note extra parens
>
> than u call like an template:
>
> foo!"a"; //equivalent = foo!("a")();
> foo!1;

I see now and thanks.

Matheus.
January 18, 2019
On Thursday, 17 January 2019 at 01:43:42 UTC, SrMordred wrote:

> struct Config
> {
> 	string title;
> 	int width;
> }
>
> struct Window
> {
> 	this(Config config)

It likely is a bad idea for a small struct like this but if it was much bigger would it makes sense to write this as :-

	this(const ref Config config)

Which is what you might do in C++ or does D handle this differently?

January 18, 2019
On Friday, 18 January 2019 at 09:39:31 UTC, John Burton wrote:
> On Thursday, 17 January 2019 at 01:43:42 UTC, SrMordred wrote:
>
>> struct Config
>> {
>> 	string title;
>> 	int width;
>> }
>>
>> struct Window
>> {
>> 	this(Config config)
>
> It likely is a bad idea for a small struct like this but if it was much bigger would it makes sense to write this as :-
>
> 	this(const ref Config config)
>
> Which is what you might do in C++ or does D handle this differently?

You'd better profile and only then act.
Seriously, whatever you know and familiar with in C++ might not work with D, you'll just end up with C++'ish code that just happens to be written in D.

D is not C++, and such premature optimizations might cause more harm if applied on occassion or out of habit.

IIRC D structs are all movable, so doing const refs here and there or using writing to ref parameter instead normal return(RVO) is likely bad for your code.

D has its own ABI, it's not even compatible with C++, so when you pass structs by value it may or may not produce similar asm as C++ compilers does.

When struct contains strings/arrays they are anyway managed by GC(unless you allocated it on your own), but this is something that more seasoned D users can explain in better details and cleaner explanation than me, if it means anything at all.

Even without all this, do you really want to mess up your codebase with implementation details about how it should do it, or write clean (self)documented code? Is it really going to be a bottleneck in your program? Passing one big struct at widget's creation on program startup and some rare events really going to kill performance?
Why not just write working code first and then identify bottlenecks and optimize when it absolutely ultimately necessary?
Btw I don't remember much ref parameters in phobos, just look at it... I found only 4 ref consts it in std.file, 3 in std.array, other modules more likely to have them in comparison operators(I have no idea why, to not introduce potential RVO case?) and in unit tests for testing correct behavior.

Again, D is not C++, what might work or even considered "good practice" there may or may not work here. So just profile it!