June 12, 2013 Re: inout template parameter, or a solution to the templated container issue | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On Wed, 12 Jun 2013 12:18:50 -0400, deadalnix <deadalnix@gmail.com> wrote: > I knew you'll have interesting feddback. > > On Wednesday, 12 June 2013 at 15:33:13 UTC, Steven Schveighoffer wrote: >> The one problem I see here is the case where you want to have Array mutate its data. >> > > I admit that this won't solve the issue. But I don't know if this is an issue to be fair. To avoid escaping pointers to your elements. This is especially important if you have total control over allocation of your elements. I would have to think about it more, but I think there is another fundamental problem with this. It feels like you could be converting const two levels deep, which is not allowed. >> For example, let's say Array makes ptr and length private overloads opIndex AND opIndexAssign to prevent taking the the address of its data. >> > > I'm not sure what is the use case. But that is clearly impossible with the solution I proposed. > >> Another issue here is, what if you don't want Array to be a template? That is, you want: >> >> struct IntArray { >> size_t length; >> int *ptr; >> } >> >> How do you make this tail-const-able? >> > > By making it a template. I'm also not sure what is the use case here, but still this is a limitation. The use case: class Container(T) { struct range { size_t length; T* ptr; } } Which I use for all dcollections containers. Yes, I could introduce it like this: struct range(inout U) if(is(U == T)) { size_t length; U *ptr; } but it feels unnecessary. I also find the usage for this solution clumsy. >> It's a very good start, and very close to the solution I have. I'm going to finish my article and post it hopefully next week. >> > > Could you give a quick executive summary ? I am hesitant to publicly announce it before releasing the article :) I don't want preconceptions for when people read it. I will send you an email. -Steve |
June 13, 2013 Re: inout template parameter, or a solution to the templated container issue | ||||
---|---|---|---|---|
| ||||
Posted in reply to Steven Schveighoffer | On Wednesday, 12 June 2013 at 19:00:28 UTC, Steven Schveighoffer wrote: > To avoid escaping pointers to your elements. This is especially important if you have total control over allocation of your elements. > Yes, that is definitively a point. > The use case: > > class Container(T) > { > > struct range > { > size_t length; > T* ptr; > } > } > > Which I use for all dcollections containers. > > Yes, I could introduce it like this: > > struct range(inout U) if(is(U == T)) > { > size_t length; > U *ptr; > } > > but it feels unnecessary. I also find the usage for this solution clumsy. > Why would you need to do it ? Why not simply do : class Container(T) { struct range { size_t length; T* ptr; } } |
June 13, 2013 Re: inout template parameter, or a solution to the templated container issue | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On Wednesday, 12 June 2013 at 13:04:28 UTC, deadalnix wrote:
> On Wednesday, 12 June 2013 at 12:37:13 UTC, monarch_dodra wrote:
>> OK, but how do you handle methods that rely on T being (potentially) mutable? For example:
>>
>> //----
>> struct Foo(inout T)
>> {
>> T a;
>> static if (isAssignable!T) //So here, "T" is actually "inout T", correct?
>> {
>> void opAssign(T other)
>> {a = other.a;}
>> }
>> }
>> //----
>>
>
> T is not assignable. If it were, you couldn't cast implicitly to Foo!const(T) . You can still return a T by reference, the caller know if it is mutable or not.
I'm not sure I understand the answer. If a parameter is marked as inout, then you are saying it must be considered as const in the entire struct?
*But*, when the user handles the struct, user may "interfere" with his own knowledge of the object's mutability?
This seems too restrictive to be useful, no? propery setters go out the window for non-escaping refs, amongst others. Implementation wise, I also don't see many usecases for useful structs that can't do mutating operations on T.
I think being able to mark which functions will operate only when T has (or lacks) specific qualifiers (because there is the same problem for functions that could only exist when T is immutable)? The problem is that the mutable->const<-immutable hierarchy is flipped on its head.
Not to mention that a template could have multiple parameters. Something like this?
struct Foo(inout T, inout U)
{
//Function body. T and U are not qualified. User may qualify as wanted.
void foo(); //normal function. T and U are "const" in here.
void bar() const; //normal const function. T and U are also "const" in here.
void foo1() inout(T); //This function requires T to be mutable. It will not appear in const
void foo2() immutable(U); //This function requires (U) to be immutable. It will not appear in const.
void foo3() inout(T) inout(U); //Both T and U need to be mutable.
void foo4() inout(T) immutable(U); //Mix and match.
}
Or something along these lines. I couldn't really see it working any other way. I mean, as presented, it feels that "Foo!T" is simply an alias for what you'd get with "Foo!(const T)", but you preserve qualification information on the outside.
|
June 13, 2013 Re: inout template parameter, or a solution to the templated container issue | ||||
---|---|---|---|---|
| ||||
Posted in reply to deadalnix | On Wed, 12 Jun 2013 22:54:19 -0400, deadalnix <deadalnix@gmail.com> wrote:
> On Wednesday, 12 June 2013 at 19:00:28 UTC, Steven Schveighoffer wrote:
>> Yes, I could introduce it like this:
>>
>> struct range(inout U) if(is(U == T))
>> {
>> size_t length;
>> U *ptr;
>> }
>>
>> but it feels unnecessary. I also find the usage for this solution clumsy.
>>
>
> Why would you need to do it ? Why not simply do :
>
> class Container(T) {
> struct range {
> size_t length;
> T* ptr;
> }
> }
if, for instance, I want to accept a container and promise not to modify the elements, I would have:
void foo(const Container!int cont);
OK, so, in order to access a range of elements from the container, the container would have some kind of function, like:
range findAll(T elem);
How to mark up this function so it's callable on a const Container?
For C++, their solution (at least for C++03, I haven't checked how this works for C++11) is to simply define iterator and const_iterator separately, and then each function is duplicated, one that returns iterator and one that is const and returns const_iterator.
But I don't like that :) I want to use inout to avoid duplication. I *also* want to have a function like this:
void processRange(R)(R r) if(isInputRange!R)
And mark up processRange so *it* states that it won't modify the data. Then I can do:
processRange(cont.findAll(1));
on a mutable C named cont.
Basically, I'm spoiled by how well D slices work :) I want the same power.
-Steve
|
Copyright © 1999-2021 by the D Language Foundation