View mode: basic / threaded / horizontal-split · Log in · Help
October 12, 2012
Re: What is the case against a struct post-blit default constructor?
On Friday, October 12, 2012 10:29:06 monarch_dodra wrote:
> On Friday, 12 October 2012 at 08:20:42 UTC, Jonathan M Davis
> 
> wrote:
> > On Friday, October 12, 2012 10:09:22 monarch_dodra wrote:
> > 
> > If that's what you're "supposed" to do, it's only because
> > opAssign is annoying
> > enough to check its invariant. Without the invariant, that's
> > not something
> > that would normally make sense to do. And it's _not_ what you
> > do with a built-
> > in type.
> > 
> > int i = void;
> > i = 5;
> > 
> > is perfectly legal. I see no reason why
> > 
> > S s = void;
> > s = S(17);
> > 
> > [SNIP]
> > 
> > - Jonathan M Davis
> 
> The issue with initializing with void actually has nothing to do
> with invariants.
> 
> Try that code defining S as RefCounted!int and see what happens.

That just means that the problem goes further than just invariants. It's still 
a big problem for invariants.

- Jonathan M Davis
October 12, 2012
Re: What is the case against a struct post-blit default constructor?
On Friday, 12 October 2012 at 08:34:31 UTC, Jonathan M Davis 
wrote:
>> 
>> Try that code defining S as RefCounted!int and see what 
>> happens.
>
> That just means that the problem goes further than just 
> invariants. It's still
> a big problem for invariants.
>
> - Jonathan M Davis

I appologize, but I don't see how this is a "big problem". It is 
no different then doing:

S s = void;
s.__ctor(args);

Initializing something to void _is_ unsafe, and must be used with 
precautions. opAssign is no different. If you want it called, 
then you _HAVE_ to make sure the target is valid first.

The only reason:
int a = void;
a = 5;

works is because a doesn't define an opAssign, and int doesn't 
have any invalid states anyways.

It's once S becomes complex that you can't just go rushing in 
assigning and constructing without proper initialization.

Using emplace makes the "problem" go away entirely*, as it will 
just do a straight-up memcopy if that is "good enough" (no extra 
cost for ints), and do "what is needed" for the rest (".init + 
.__ctor" or ".init + opAssign").

alias RefCounted!int S;
int i = void;
S s1 = void;
S s2 = void;
emplace(&i, 5);     //OK! Do a memcpy assignement
emplace(&s1, 5);    //OK! Do a .init memcpy + .__ctor
emplace(&s2, S(5)); //OK! Do a .init memcpy + .opAssign**

*Technically, once my fix goes through. It currently chokes.
**Actually, RefCounted has a CC, so that is the one that will be 
used. Just wanted to illustrate it *could* be one of the things 
that could happen.

//--------
Bask on subject, I _have_ started working with invariants. I 
think they are nice, but there indeed some times where you'd wish 
they wouldn't trigger.

How about the @noinvariant function attribute? Sounds like a 
simple enough solution.

At that point, the developer can just insert "assert(&this);" in 
said functions, if and where he judges it necessary.
October 12, 2012
Re: What is the case against a struct post-blit default constructor?
On Friday, October 12, 2012 11:31:36 monarch_dodra wrote:
> Bask on subject, I _have_ started working with invariants. I
> think they are nice, but there indeed some times where you'd wish
> they wouldn't trigger.
> 
> How about the @noinvariant function attribute? Sounds like a
> simple enough solution.
> 
> At that point, the developer can just insert "assert(&this);" in
> said functions, if and where he judges it necessary.

That sounds like a decent solution to me, but I think that there's a good 
chance that Walter would reject it on principle (since in general, skipping 
the invariant pretty much defeats the purpose of having one). This is the only 
case that I'm aware of where it really makes sense to not have an invariant 
triggered, and he seems to be against the idea that opAssign wouldn't trigger 
the invariant when called, so I expect that he'd be against this as well if 
the whole purpose was to enable that case. Other, solid use case would 
probably be needed as well.

- Jonathan M Davis
October 12, 2012
Re: What is the case against a struct post-blit default constructor?
On Friday, 12 October 2012 at 09:42:19 UTC, Jonathan M Davis 
wrote:
> That sounds like a decent solution to me, but I think that 
> there's a good
> chance that Walter would reject it on principle (since in 
> general, skipping
> the invariant pretty much defeats the purpose of having one).

But he already suggested implementing a _custom_ mechanism for 
skipping the invariant somewhere else in this thread (i.e. a 
"valid" flag) which is arguably even worse…

David
October 12, 2012
Re: What is the case against a struct post-blit default constructor?
On Friday, October 12, 2012 11:49:48 David Nadlinger wrote:
> On Friday, 12 October 2012 at 09:42:19 UTC, Jonathan M Davis
> 
> wrote:
> > That sounds like a decent solution to me, but I think that
> > there's a good
> > chance that Walter would reject it on principle (since in
> > general, skipping
> > the invariant pretty much defeats the purpose of having one).
> 
> But he already suggested implementing a _custom_ mechanism for
> skipping the invariant somewhere else in this thread (i.e. a
> "valid" flag) which is arguably even worse…

Clearly, I missed that. But that's definitely not particularly clean (though it 
_can_ be done right now without making any changes to the language, which for 
most things is the better approach). It's not as bad now that with have 
version(assert), since it makes it so that the valid flag can be compiled out 
in release mode, but it's still messier than @noinvariant would be (if nothing 
else, it requires more code and a version(assert) block every time that the 
valid flag is used), and more importantly, it only solves the T.init case and 
not the case where the struct is initialized to void. So, @noinvariant makes a 
lot more sense, but it _does_ require an update the language, so I wouldn't 
expect Walter to be all that enthused about it, but if he already thinks that 
a "valid" flag is okay, then he wouldn't necessarily be opposed to the idea of 
it being possible to explicitly skip invariant checks in some cases.

- Jonathan M Davis
October 12, 2012
Re: What is the case against a struct post-blit default constructor?
On Friday, 12 October 2012 at 08:20:42 UTC, Jonathan M Davis 
wrote:

> Really, I think that it's a bad design decision to require that 
> the invariant be called before opAssign. It does _not_ play 
> nice with some of D's other features, and the result is likely 
> to be that invariants get used less, meaning that code is more 
> likely to be buggy.

 You make a good argument, but you can also override opAssign for 
things that are not it's type exact type. So...

 struct S {
   float x;

   ref S opAssign(int y) {
     x = y;
     return this;
   }
 }

 S s;
 int i;
 s = i; //opAssign, correct?


 In cases like this opAssign would need an invariant before and 
after the call. But if you were just replacing the whole object 
you wouldn't.

 I'll say a @novariant is the better answer, and automatically 
used on the default copy/opAssign/postblitz (before the call, but 
still needed after).
October 12, 2012
Re: What is the case against a struct post-blit default constructor?
On Friday, 12 October 2012 at 20:33:11 UTC, Era Scarecrow wrote:
>  I'll say a @novariant is the better answer, and automatically 
> used on the default copy/opAssign/postblitz (before the call, 
> but still needed after).

The language already states that the invariant is only called at 
the end of construction (ergo copy/postblit).

opAssign, IMO, is not (much) different than any other function. 
invariant checks should be disabled on it, if and when the 
developer explicitly requests it. That's the safest route anyway.
October 15, 2012
Re: What is the case against a struct post-blit default constructor?
On Thu, 11 Oct 2012 13:23:10 -0400, Jonathan M Davis <jmdavisProg@gmx.com>  
wrote:

> Any situation where the init value is essentially invalid (like it would  
> be
> with floating point types) makes it so that you can't have an invariant,  
> and in
> many of those cases, having a default constructor which was always called
> would solve the problem. I'm still in favor of _not_ trying to add  
> default
> constructors given all of the issues involved, and I agree that on the  
> whole,
> init is a superior solution (even if it isn't perfect), but there _are_  
> cases
> where you can't have an invariant because of it.


Isn't it possible to customize when an "invariant" is called using  
contracts?

For example:

struct S
{
   private bool isValid;
   private void _invariant() {assert(isValid);}

   void foo()
   in { _invariant();} out {_invariant();} body
   {
      // whatever
   }

   void opAssign(ref S other)
   out {_invariant();} body
   {
      isValid = other.isValid;
   }
}

???

Yeah, It's extra work.  But essentially, isn't this what you want?  The  
thing about disabling invariant checks on some specific function in some  
specific case is that someone else has a valid case for requiring it.

The only sucky part about the above is, _invariant is compiled in even in  
release mode (though it should inline to a noop).

-Steve
October 15, 2012
Re: What is the case against a struct post-blit default constructor?
On Monday, 15 October 2012 at 15:56:33 UTC, Steven Schveighoffer 
wrote:
>
>
> Isn't it possible to customize when an "invariant" is called 
> using contracts?
>
> For example:
>
> struct S
> {
>    private bool isValid;
>    private void _invariant() {assert(isValid);}
>
>    void foo()
>    in { _invariant();} out {_invariant();} body
>    {
>       // whatever
>    }
>
>    void opAssign(ref S other)
>    out {_invariant();} body
>    {
>       isValid = other.isValid;
>    }
> }
>
> ???
>
> Yeah, It's extra work.  But essentially, isn't this what you 
> want?  The thing about disabling invariant checks on some 
> specific function in some specific case is that someone else 
> has a valid case for requiring it.
>
> The only sucky part about the above is, _invariant is compiled 
> in even in release mode (though it should inline to a noop).
>
> -Steve

There is now an "assert" word for version blocks so you can put 
your code inside that, and it doesn't get compiled in during 
release.
October 16, 2012
Re: What is the case against a struct post-blit default constructor?
On Monday, October 15, 2012 11:56:33 Steven Schveighoffer wrote:
> Yeah, It's extra work. But essentially, isn't this what you want? The
> thing about disabling invariant checks on some specific function in some
> specific case is that someone else has a valid case for requiring it.

I've considered it, and I may end up doing that for SysTime, but it's also 
kind of ridiculous to have to add assertions to _every_ function like that 
just to avoid having it called on one function.

> The only sucky part about the above is, _invariant is compiled in even in
> release mode (though it should inline to a noop).

version(assert) now fixes that problem.
Next ›   Last »
1 2 3 4 5
Top | Discussion index | About this forum | D home