* Re: Prohibiting dynamic allocation for the given type
2008-03-19 8:24 ` Maciej Sobczak
@ 2008-03-19 11:31 ` Georg Bauhaus
2008-03-19 13:13 ` gpriv
` (2 subsequent siblings)
3 siblings, 0 replies; 20+ messages in thread
From: Georg Bauhaus @ 2008-03-19 11:31 UTC (permalink / raw)
Maciej Sobczak schrieb:
> On 19 Mar, 04:06, gp...@axonx.com wrote:
>
>> Ada is high level language
>
> That does not matter.
> Ada is a high level language, but still provides two ways to create an
> object:
>
> X : Type;
> Y : Type_Ptr := new Type;
>
> If these two methods are available, then apparently there is a
> difference between them and this difference is not in *where* objects
> are created, but *how long* they are allowed to live.
>
> The high-level part of Ada can hide the "where" part, but not "how
> long".
With Ada 2005, you can try types derived in nested
scopes. No objects of a more deeply nested type can
leave these scopes (pointers to parent'class cannot be
used as an escape).
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Prohibiting dynamic allocation for the given type
2008-03-19 8:24 ` Maciej Sobczak
2008-03-19 11:31 ` Georg Bauhaus
@ 2008-03-19 13:13 ` gpriv
2008-03-19 13:54 ` Maciej Sobczak
2008-03-19 16:37 ` Eric Hughes
2008-03-19 22:17 ` Georg Bauhaus
3 siblings, 1 reply; 20+ messages in thread
From: gpriv @ 2008-03-19 13:13 UTC (permalink / raw)
On Mar 19, 4:24 am, Maciej Sobczak <see.my.homep...@gmail.com> wrote:
> On 19 Mar, 04:06, gp...@axonx.com wrote:
>
> > Ada is high level language
>
> That does not matter.
> Ada is a high level language, but still provides two ways to create an
> object:
>
> X : Type;
> Y : Type_Ptr := new Type;
>
> If these two methods are available, then apparently there is a
> difference between them and this difference is not in *where* objects
> are created, but *how long* they are allowed to live.
>
> The high-level part of Ada can hide the "where" part, but not "how
> long".
>
> For some types I might want to prohibit one of these two ways of
> object creation.
>
> Prohibiting the first one is easy with limited types that have unknown
> discriminants + factory functions that return pointers.
>
> Prohibiting the second one seems to be impossible.
> For me this is a limitation.
>
> > What's wrong with good old comments atop of type declaration.
>
> What's wrong with C with good comments? :-)
Nothing..
>
> > But if you insist you may do run-time check by
> > defining custom Storage_Pool pool for your object
>
> Storage_Pool is a property of the pointer, not the type.
Are you asking for solutions or simply trolling?
>
> --
> Maciej Sobczak *www.msobczak.com*www.inspirel.com
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Prohibiting dynamic allocation for the given type
2008-03-19 13:13 ` gpriv
@ 2008-03-19 13:54 ` Maciej Sobczak
0 siblings, 0 replies; 20+ messages in thread
From: Maciej Sobczak @ 2008-03-19 13:54 UTC (permalink / raw)
On 19 Mar, 14:13, gp...@axonx.com wrote:
> > Storage_Pool is a property of the pointer, not the type.
>
> Are you asking for solutions or simply trolling?
I'm discussing things in public to ensure that I have a good
understanding of various language features and corners.
This is simply necessary to actually use the language in a responsible
way.
Building technical confidence and trolling are different things.
BTW - After seeing your responses and thinking more about the problem
itself I think that documenting the expected usage patterns gives
better results when compared to required effort - especially if the
solution would complicate the intended usage pattern as well.
--
Maciej Sobczak * www.msobczak.com * www.inspirel.com
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Prohibiting dynamic allocation for the given type
2008-03-19 8:24 ` Maciej Sobczak
2008-03-19 11:31 ` Georg Bauhaus
2008-03-19 13:13 ` gpriv
@ 2008-03-19 16:37 ` Eric Hughes
2008-03-20 0:48 ` Robert A Duff
2008-03-19 22:17 ` Georg Bauhaus
3 siblings, 1 reply; 20+ messages in thread
From: Eric Hughes @ 2008-03-19 16:37 UTC (permalink / raw)
On Mar 19, 2:24 am, Maciej Sobczak <see.my.homep...@gmail.com> wrote:
> X : Type;
> Y : Type_Ptr := new Type;
[...]
> For some types I might want to prohibit one of these two ways of
> object creation.
You can't prohibit the object creation itself, but you can make such
an object useless.
> If these two methods are available, then apparently there is a
> difference between them and this difference is not in *where* objects
> are created, but *how long* they are allowed to live.
If you want to control lifetime, you can control either the allocation
lifetime or the functional lifetime; the second is a subset of the
first. As you've pointed out, you don't get a lot of control over
allocation lifetime. But functional lifetime you can control. Here's
how I would approach it.
First, make the utility of your type depend for its function on the
existence of some other object. This likely goes into a constructor
function, but need not. Your type would contain some kind of a weak
accessor, one that doesn't enforce the existence of its referent.
Gate all utility upon the existence of this referent. If the referent
is absent, do nothing or throw an exception, whatever. This check is
cheap; it's that some access value is not null. Use finalization in
the referent to update the accessor value in your type. This gives
you a dependency between objects, regardless of how the referent
object is allocated.
Second, when instantiating the object, make it refer to a sentry
object with block scope. When the sentry object goes out of scope,
your original object will stop working. I frequently use nested
declare/begin/end blocks within subprogram definitions to trigger
sentry finalization.
You'll still have to document that if you want scope lifetime for your
object's utility, you'll have to make it dependent upon a scoped
variable. But at that point the code explicitly captures an intention
for scope lifetime.
Eric
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Prohibiting dynamic allocation for the given type
2008-03-19 16:37 ` Eric Hughes
@ 2008-03-20 0:48 ` Robert A Duff
2008-03-20 21:35 ` Eric Hughes
0 siblings, 1 reply; 20+ messages in thread
From: Robert A Duff @ 2008-03-20 0:48 UTC (permalink / raw)
Eric Hughes <eric.eh9@gmail.com> writes:
> If you want to control lifetime, you can control either the allocation
> lifetime or the functional lifetime; the second is a subset of the
> first. As you've pointed out, you don't get a lot of control over
> allocation lifetime. But functional lifetime you can control. Here's
> how I would approach it.
>
> First, make the utility of your type depend for its function on the
> existence of some other object. This likely goes into a constructor
> function, but need not. Your type would contain some kind of a weak
> accessor, one that doesn't enforce the existence of its referent.
> Gate all utility upon the existence of this referent. If the referent
> is absent, do nothing or throw an exception, whatever. This check is
> cheap; it's that some access value is not null. Use finalization in
> the referent to update the accessor value in your type. This gives
> you a dependency between objects, regardless of how the referent
> object is allocated.
>
> Second, when instantiating the object, make it refer to a sentry
> object with block scope. When the sentry object goes out of scope,
> your original object will stop working. I frequently use nested
> declare/begin/end blocks within subprogram definitions to trigger
> sentry finalization.
>
> You'll still have to document that if you want scope lifetime for your
> object's utility, you'll have to make it dependent upon a scoped
> variable. But at that point the code explicitly captures an intention
> for scope lifetime.
I don't understand this method. Maybe you could give an outline of the
code. In particular, how do you ensure that the "sentry" is not
heap-allocated? If you trust the client on that, then why not trust
the client to avoid heap allocation for the "real" object in the
first place? I must be missing something...
- Bob
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Prohibiting dynamic allocation for the given type
2008-03-20 0:48 ` Robert A Duff
@ 2008-03-20 21:35 ` Eric Hughes
0 siblings, 0 replies; 20+ messages in thread
From: Eric Hughes @ 2008-03-20 21:35 UTC (permalink / raw)
On Mar 19, 6:48 pm, Robert A Duff <bobd...@shell01.TheWorld.com>
wrote:
> Maybe you could give an outline of the code.
I'll presume the as-yet-unpublished Strong_Access and Weak_Access
(that's what I'm working on now) (think: like Boost shared_ptr and
weak_ptr). Here's a bare outline.
type X is null record ; -- nonce type
package Smart_Access_to_X is new Smart_Access( X ) ;
use Smart_Access_to_X ;
-- type Object is contingent upon one of its components for some or
all of its functionality
type Object is record
Lifetime : Weak_Access ;
-- ...
end record ;
...
procedure Operation( Obj : in out Object )
is begin
if ( Is_Null( Obj.Lifetime ) ) then return ; end if ;
-- Assert Lifetime referent continues to exist
-- ...
end ;
...
procedure Foo
is
Bar : Object_Access := new Object'( Lifetime =>
Null_Weak_Access ) ;
-- Bar does not work, can't make it do anything.
begin
declare
Sentry : Strong_Access := Construct_Strong_Access( Access_Value
=> new X ) ;
begin
Bar.all := Object'( Lifetime =>
Copy_As_Weak_Access( Strong_Accessor => Sentry ) ) ;
-- Bar now works
end
-- Sentry is now out of scope; referent implicitly deallocated;
weak reference to it is null; Bar has stopped working
end
> In particular, how do you ensure that the "sentry" is not heap-allocated?
As I said in my original post, if you want scope lifetime, you need to
instantiate a scope dependency. This technique does not enforce any
particular lifetime policy.
> If you trust the client on that, then why not trust
> the client to avoid heap allocation for the "real" object in the
> first place?
The distinction here is that the original object still exists. Access
values to it will remain valid. Depending on how the object is used,
it might be necessary to have that. Personally, I can't imagine how
this might be useful unless two or more tasks are at issue, say,
passing a 'not null access all' parameter to a subprogram that assumes
continued existence of the referent of its parameter. Since the
dependency mechanism is generic, it would be just as possible to make
the functional lifetime of a variable have the scope of some task.
The original question was somewhat theoretical, as is this answer.
Eric
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Prohibiting dynamic allocation for the given type
2008-03-19 8:24 ` Maciej Sobczak
` (2 preceding siblings ...)
2008-03-19 16:37 ` Eric Hughes
@ 2008-03-19 22:17 ` Georg Bauhaus
2008-03-19 23:40 ` gpriv
2008-03-20 21:11 ` Maciej Sobczak
3 siblings, 2 replies; 20+ messages in thread
From: Georg Bauhaus @ 2008-03-19 22:17 UTC (permalink / raw)
Maciej Sobczak wrote:
> On 19 Mar, 04:06, gp...@axonx.com wrote:
>
>> Ada is high level language
>
> That does not matter.
> Ada is a high level language, but still provides two ways to create an
> object:
>
> X : Type;
> Y : Type_Ptr := new Type;
>
> If these two methods are available, then apparently there is a
> difference between them and this difference is not in *where* objects
> are created, but *how long* they are allowed to live.
>
> The high-level part of Ada can hide the "where" part, but not "how
> long".
(Somehow a posting of today is only visible through Google,
but not via the news server, apologies if this a repeated
remark.)
Using Ada 2005, you can have some of the "how long" control.
Deriving in nested scopes will limit the objects to that
scope. This includes heap objects. For example, you cannot
use pointers to Parent'Class to refer to objects of inner
scope types.
package body News10 is
use Ada;
procedure Scope is
type T is new Finalization.Controlled with null record;
type T_Ptr is access all T;
overriding
procedure Finalize(Object: in out T) is
-- just show what is going on
begin
Text_IO.Put_Line("Finalized");
end Finalize;
X: T_Ptr;
begin
Text_IO.Put_Line("Enter");
X := new T;
pragma Inspection_Point(X);
Text_IO.Put_Line("Exit");
end Scope;
end News10;
with News10;
procedure Test_News10 is
begin
loop
News10.Scope;
delay 2.0;
end loop;
end;
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Prohibiting dynamic allocation for the given type
2008-03-19 22:17 ` Georg Bauhaus
@ 2008-03-19 23:40 ` gpriv
2008-03-20 21:11 ` Maciej Sobczak
1 sibling, 0 replies; 20+ messages in thread
From: gpriv @ 2008-03-19 23:40 UTC (permalink / raw)
On Mar 19, 6:17 pm, Georg Bauhaus <rm.tsoh.plus-
bug.bauh...@maps.futureapps.de> wrote:
> Maciej Sobczak wrote:
> > On 19 Mar, 04:06, gp...@axonx.com wrote:
>
> >> Ada is high level language
>
> > That does not matter.
> > Ada is a high level language, but still provides two ways to create an
> > object:
>
> > X : Type;
> > Y : Type_Ptr := new Type;
>
> > If these two methods are available, then apparently there is a
> > difference between them and this difference is not in *where* objects
> > are created, but *how long* they are allowed to live.
>
> > The high-level part of Ada can hide the "where" part, but not "how
> > long".
>
> (Somehow a posting of today is only visible through Google,
> but not via the news server, apologies if this a repeated
> remark.)
>
> Using Ada 2005, you can have some of the "how long" control.
> Deriving in nested scopes will limit the objects to that
> scope. This includes heap objects. For example, you cannot
> use pointers to Parent'Class to refer to objects of inner
> scope types.
>
> package body News10 is
>
> use Ada;
>
> procedure Scope is
> type T is new Finalization.Controlled with null record;
> type T_Ptr is access all T;
>
> overriding
> procedure Finalize(Object: in out T) is
> -- just show what is going on
> begin
> Text_IO.Put_Line("Finalized");
> end Finalize;
>
> X: T_Ptr;
> begin
> Text_IO.Put_Line("Enter");
> X := new T;
> pragma Inspection_Point(X);
> Text_IO.Put_Line("Exit");
> end Scope;
>
> end News10;
>
> with News10;
> procedure Test_News10 is
> begin
> loop
> News10.Scope;
> delay 2.0;
> end loop;
> end;
In that case one has to derive the type from parent within the scope
of interest. Does it really improve clarity? And what will prevent
others from using instances of parent class (unless it's abstract).
For the sake of argument let's consider example where data
transmission should be uninterrupted by competing threads:
declare
Lock : Lock_T (Io.Frame_Mutex);
begin
Io.Put_Line
(':' &
Trim (Device_Count'Image (Idx - 1), Both) &
" frame bin " &
Image_Size_T'Image (Frame (Frm).Size));
Io.Write (Frame (Frm).Image);
Io.Put_Line ("");
end;
Lock is referenced only once and intent is pretty obvious. If one
would want to lock mutex for longer time the solution would be:
Cmd.Frame_Mutex.Enter;
...
Cmd.Frame_Mutex.Leave;
In C++ one can privatize new/delete overrides to prevent object from
heap allocation. However, it won't prevent some ignorant user from
statically allocating the object deadlocking for the lifetime of the
entire application.
It's all false sense of security IMHO.
George.
^ permalink raw reply [flat|nested] 20+ messages in thread
* Re: Prohibiting dynamic allocation for the given type
2008-03-19 22:17 ` Georg Bauhaus
2008-03-19 23:40 ` gpriv
@ 2008-03-20 21:11 ` Maciej Sobczak
1 sibling, 0 replies; 20+ messages in thread
From: Maciej Sobczak @ 2008-03-20 21:11 UTC (permalink / raw)
On 19 Mar, 23:17, Georg Bauhaus <rm.tsoh.plus-
bug.bauh...@maps.futureapps.de> wrote:
> Using Ada 2005, you can have some of the "how long" control.
> Deriving in nested scopes will limit the objects to that
> scope.
Yes, but this assumes that the user of the type is also implementing
it - in the same scope.
I was considering the type implemented in the library and the
possibility to enforce some given lifetime pattern at the use site.
Deriving in nested scope requires to mix the responsibilities of
"provider" and "user" and this is not possible when "provider" is a
library.
--
Maciej Sobczak * www.msobczak.com * www.inspirel.com
^ permalink raw reply [flat|nested] 20+ messages in thread