Topic: Type checking of default arguments, was Allocators -- Argh!


Author: Max TenEyck Woodbury <mtew@cds.duke.edu>
Date: 1996/09/27
Raw View
James Kanze US/ESC 60/3/141 #40763 wrote:
>
> In article <32480106.408@cds.duke.edu> Max TenEyck Woodbury
> <mtew@cds.duke.edu> writes:
>
> |> J. Kanze wrote:
> |> >
> |> > In the discussions concerning allocators, one question has occured to
> |> > me: when is the type checking of a default argument done.  (Or more
> |> > precisely, is it done if the default argument is never used.)
> |> >
> |> > The reason I ask is simple: all of the copy constructors in the
> |> > container section of the draft standard, and that of string, in fact
> |> > take an additional parameter: an allocator.  And that allocator (whose
> |> > type is an argument to the template) has a default argument of
> |> > allocator(), e.g. an expression of type "allocator" (the default
> |> > allocator).
> |> >
> |> > If, as I have believed until now, the default argument must have a type
> |> > suitable for actual use, even if it is not used, then a user provided
> |> > allocator *must* provide a constructor which can be called with the
> |> > default allocator, even if there are never any objects created which use
> |> > the default argument.
> |> >
> |> > Am I mistaken, and type checking will only occur on use, or is it the
> |> > committee's intent that all user defined allocators have this
> |> > constructor.  (If the latter, it would be a good idea if this was
> |> > mentioned in the requirements section on allocators.)
> |> >
>
> First: I have just noticed that I misread the draft.  The default
> argument for the allocator parameter is Allocator() (the default
> constructor of the Allocator instantiation type), not allocator() (the
> default constructor of the default allocator type).  So the time
> mismatch that I was referring to cannot occur (and user allocators do
> not, in fact, need a constructor taking an "allocator").
>

Actually, the constructor is not passed, the result of the constructor
call is passed and that would be an instance of the correct type of
allocator, and usually but not necessarily it would be passed by
reference.

> This still doesn't help with the basic problem, though.  How does a
> container class (say vector) ensure that all of its contained elements
> (say, string) use the correct value of its own allocator, and not the
> default value?
>

Yes, it's a mess.  If run time differentiation was not allowed, it
would not matter which run time instance of the allocator was
passed.  As discussed in another thread, run time differentiation
is a fix on a fix to get around the absence of wide implementation
of a template feature.  If defaults for template parameter types is
implemented, the original fix would no longer be needed and the
second level fix would not be needed either.

> |> I believe the answer is that it is checked at various times.  One
> |> of the checks is at 'template time'.  It also gets checked at
> |> 'override selection time'.  The fact that an argument has a default
> |> value complicates the actual checking, but doesn't change when the
> |> checking is done.
>
> Actually, on further study, I think that the checking in this case is
> *only* done if the default argument is actually used.
>
> |> Yes, the argument must be one suitable for actual use and the
> |> container or string constructor has to copy the allocator parameter
> |> to the allocator in its constructor.  That is, the last part of the
> |> first container or string constructor line should be ..., Allocator &a)
> |> allocator(a) ... .  However the default null and copy constructors
> |> for a class would be sufficient in most cases so the user is not
> |> required to provide a special one unless it is needed.  If a string
> |> or container definition failed to make at least this much use of
> |> the parameter, I don't see how the implementation could be
> |> considered conforming.  (I'd like to see this paramter and all the
> |> bagage it caries eliminated.)
>
> I think I agree.  However, my question was: given "vector< list< X ,
> MyAlloc > , MyAlloc >", and supposing that all instances of MyAlloc
> don't compare equal (because, e.g. they refer to different shared memory
> arenas), how does vector ensure that all of the contained lists use
> *its* allocator; i.e.: that all of the contained objects allocate memory
> from the same arena?
>

You'd have to write partial specializations for the vector and list
constructors.  (Yet another reason for killing this feature.)

> As I see it, if all instances of a given allocator type are not required
> to compare equal, then vector must be able to handle three different
> cases:
>
> 1. The contained elements use no dynamic memory (e.g. vector< int >).
> In this case, it must create them using the normal copy constructor.
>

As it stands, it still has to pass an allocator instance, even if it
is an empty structure.

> 2. The contained elements use dynamic memory, and use the vector
> specific allocator class to allocate it (e.g. list< int , MyAlloc >).
> In this case, vector must pass its instance of the allocator, as well as
> the initial value, when creating contained elements.
>
> 3. The contained elements use dynamic memory, but do not use the vector
> specific allocator class to allocate it.  In this case, nothing is going
> to work.
>
> |> Some of the confusion is the STL usage of empty objects.  A
> |> number of classes in the STL (and elsewhere) do not instance
> |> any storage.  By default, allocators are one of these.  That
> |> does NOT mean they are not objects.  They will have an offset
> |> in the object structure even if they have no extent.  (This
> |> means that a compiler where a sloppy implementer never
> |> considered the case of empty objects, might just blow up
> |> when it runs across one of these 'anomolies'.)  They
> |> may also have an impact on the alignment of other objects.
>
> In cases like traits, however, the empty type is only used to
> instantiate the template.  There is no instance of it in the
> instantiated object.  Following your argument, the allocator types would
> fall into this category.
>

Actually, the trait object does exist inside each instance, but
being an empty structure, its impact on the object is minimal.
Proper allocators would be similar, but run-time differentiable
allocators do not necessiarily have this no storage used property.

> |> On th original topic:  One of the more obtuse parts of the
> |> implementation of templates is their interaction with override
> |> selection.  Frankly, I don't know exactly how it works.
> |> My understanding of the specs is that failure to get an exact
> |> type match on the paramters for an function or operator
> |> triggers a search for suitable conversion routine by the
> |> compiler, including the possible generation of such a routine
> |> by the template facility.  (If it generates a diagnostic
> |> instead, the user is most likely to stick in an appropriate
> |> cast.  In either case, the search and template instentation
> |> gets done eventually.)  That means that a call to a conversion
> |> routine will be generated (one way or another) by the compiler
> |> if the types do not match.  I contend that these conversion
> |> routines are the correct place to make deep or shallow copy
> |> decisions.  Allowing for a run time test on allocators moves
> |> these decisions out of the domain of the conversion routines
> |> into the main stream of algorithm design and user code.
>
> The problem is that these conversion routines must exist if they are to
> be called by the compiler.  For the moment, they don't, although it
> would not be difficult to imagine that all containers had a constructor:
>
>         template< class Other >
>                                                 container( Other const& o )
>         {
>                 initialize( o.begin() , o.end() ) ;
>         }
>
> This would mean that any container following the STL conventions could
> be used to initialize any other.  (Would this cause any ambiguity
> problems with the copy constructor?)

I'm not sure exactly what the syntax would be, but it would
specify a member template function for a copy constructor with
the parameter being a type identical with the string or container
type except that the allocator would be the only template parameter.

I've probably got some of the details wrong but I expect that
it would look something like this -- ('...' used in the meta sense,
not in the C++ linguistic sense)

template < T, ..., Allocator=allocator>
  class container : public Allocator {
    ...
    template <alloc> container( container< T, ..., alloc> &) {
      ... };
    ...
}

This would only get activated when a different allocator was
specified and would not open up the general conversion can of
worms.

mtew@cds.duke.edu
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: kanze@gabi-soft.fr (J. Kanze)
Date: 1996/09/23
Raw View
In the discussions concerning allocators, one question has occured to
me: when is the type checking of a default argument done.  (Or more
precisely, is it done if the default argument is never used.)

The reason I ask is simple: all of the copy constructors in the
container section of the draft standard, and that of string, in fact
take an additional parameter: an allocator.  And that allocator (whose
type is an argument to the template) has a default argument of
allocator(), e.g. an expression of type "allocator" (the default
allocator).

If, as I have believed until now, the default argument must have a type
suitable for actual use, even if it is not used, then a user provided
allocator *must* provide a constructor which can be called with the
default allocator, even if there are never any objects created which use
the default argument.

Am I mistaken, and type checking will only occur on use, or is it the
committee's intent that all user defined allocators have this
constructor.  (If the latter, it would be a good idea if this was
mentioned in the requirements section on allocators.)

--
James Kanze           (+33) 88 14 49 00          email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs Bourgeois, 67000 Strasbourg, France
Conseils en informatique industrielle --
                            -- Beratung in industrieller Datenverarbeitung


[ comp.std.c++ is moderated.  To submit articles: try just posting with      ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu         ]
[ FAQ:      http://reality.sgi.com/employees/austern_mti/std-c++/faq.html    ]
[ Policy:   http://reality.sgi.com/employees/austern_mti/std-c++/policy.html ]
[ Comments? mailto:std-c++-request@ncar.ucar.edu                             ]





Author: Jason Merrill <jason@cygnus.com>
Date: 1996/09/24
Raw View
>>>>> kanze@gabi-soft.fr (J. Kanze) writes:

> The reason I ask is simple: all of the copy constructors in the
> container section of the draft standard, and that of string, in fact
> take an additional parameter: an allocator.  And that allocator (whose
> type is an argument to the template) has a default argument of
> allocator(), e.g. an expression of type "allocator" (the default
> allocator).

Actually, in the current draft it is Allocator() (the template parameter).

Jason
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: Max TenEyck Woodbury <mtew@cds.duke.edu>
Date: 1996/09/24
Raw View
J. Kanze wrote:
>
> In the discussions concerning allocators, one question has occured to
> me: when is the type checking of a default argument done.  (Or more
> precisely, is it done if the default argument is never used.)
>
> The reason I ask is simple: all of the copy constructors in the
> container section of the draft standard, and that of string, in fact
> take an additional parameter: an allocator.  And that allocator (whose
> type is an argument to the template) has a default argument of
> allocator(), e.g. an expression of type "allocator" (the default
> allocator).
>
> If, as I have believed until now, the default argument must have a type
> suitable for actual use, even if it is not used, then a user provided
> allocator *must* provide a constructor which can be called with the
> default allocator, even if there are never any objects created which use
> the default argument.
>
> Am I mistaken, and type checking will only occur on use, or is it the
> committee's intent that all user defined allocators have this
> constructor.  (If the latter, it would be a good idea if this was
> mentioned in the requirements section on allocators.)
>

(James, I think you know all that I'm about to say on this.  I'm
just putting it down for the record.)

I believe the answer is that it is checked at various times.  One
of the checks is at 'template time'.  It also gets checked at
'override selection time'.  The fact that an argument has a default
value complicates the actual checking, but doesn't change when the
checking is done.

Yes, the argument must be one suitable for actual use and the
container or string constructor has to copy the allocator parameter
to the allocator in its constructor.  That is, the last part of the
first container or string constructor line should be ..., Allocator &a)
allocator(a) ... .  However the default null and copy constructors
for a class would be sufficient in most cases so the user is not
required to provide a special one unless it is needed.  If a string
or container definition failed to make at least this much use of
the parameter, I don't see how the implementation could be
considered conforming.  (I'd like to see this paramter and all the
bagage it caries eliminated.)

Some of the confusion is the STL usage of empty objects.  A
number of classes in the STL (and elsewhere) do not instance
any storage.  By default, allocators are one of these.  That
does NOT mean they are not objects.  They will have an offset
in the object structure even if they have no extent.  (This
means that a compiler where a sloppy implementer never
considered the case of empty objects, might just blow up
when it runs across one of these 'anomolies'.)  They
may also have an impact on the alignment of other objects.

On th original topic:  One of the more obtuse parts of the
implementation of templates is their interaction with override
selection.  Frankly, I don't know exactly how it works.
My understanding of the specs is that failure to get an exact
type match on the paramters for an function or operator
triggers a search for suitable conversion routine by the
compiler, including the possible generation of such a routine
by the template facility.  (If it generates a diagnostic
instead, the user is most likely to stick in an appropriate
cast.  In either case, the search and template instentation
gets done eventually.)  That means that a call to a conversion
routine will be generated (one way or another) by the compiler
if the types do not match.  I contend that these conversion
routines are the correct place to make deep or shallow copy
decisions.  Allowing for a run time test on allocators moves
these decisions out of the domain of the conversion routines
into the main stream of algorithm design and user code.

mtew@cds.duke.edu
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]





Author: kanze@lts.sel.alcatel.de (James Kanze US/ESC 60/3/141 #40763)
Date: 1996/09/25
Raw View
In article <32480106.408@cds.duke.edu> Max TenEyck Woodbury
<mtew@cds.duke.edu> writes:

|> J. Kanze wrote:
|> >
|> > In the discussions concerning allocators, one question has occured to
|> > me: when is the type checking of a default argument done.  (Or more
|> > precisely, is it done if the default argument is never used.)
|> >
|> > The reason I ask is simple: all of the copy constructors in the
|> > container section of the draft standard, and that of string, in fact
|> > take an additional parameter: an allocator.  And that allocator (whose
|> > type is an argument to the template) has a default argument of
|> > allocator(), e.g. an expression of type "allocator" (the default
|> > allocator).
|> >
|> > If, as I have believed until now, the default argument must have a type
|> > suitable for actual use, even if it is not used, then a user provided
|> > allocator *must* provide a constructor which can be called with the
|> > default allocator, even if there are never any objects created which use
|> > the default argument.
|> >
|> > Am I mistaken, and type checking will only occur on use, or is it the
|> > committee's intent that all user defined allocators have this
|> > constructor.  (If the latter, it would be a good idea if this was
|> > mentioned in the requirements section on allocators.)
|> >

First: I have just noticed that I misread the draft.  The default
argument for the allocator parameter is Allocator() (the default
constructor of the Allocator instantiation type), not allocator() (the
default constructor of the default allocator type).  So the time
mismatch that I was referring to cannot occur (and user allocators do
not, in fact, need a constructor taking an "allocator").

This still doesn't help with the basic problem, though.  How does a
container class (say vector) ensure that all of its contained elements
(say, string) use the correct value of its own allocator, and not the
default value?

|> I believe the answer is that it is checked at various times.  One
|> of the checks is at 'template time'.  It also gets checked at
|> 'override selection time'.  The fact that an argument has a default
|> value complicates the actual checking, but doesn't change when the
|> checking is done.

Actually, on further study, I think that the checking in this case is
*only* done if the default argument is actually used.

|> Yes, the argument must be one suitable for actual use and the
|> container or string constructor has to copy the allocator parameter
|> to the allocator in its constructor.  That is, the last part of the
|> first container or string constructor line should be ..., Allocator &a)
|> allocator(a) ... .  However the default null and copy constructors
|> for a class would be sufficient in most cases so the user is not
|> required to provide a special one unless it is needed.  If a string
|> or container definition failed to make at least this much use of
|> the parameter, I don't see how the implementation could be
|> considered conforming.  (I'd like to see this paramter and all the
|> bagage it caries eliminated.)

I think I agree.  However, my question was: given "vector< list< X ,
MyAlloc > , MyAlloc >", and supposing that all instances of MyAlloc
don't compare equal (because, e.g. they refer to different shared memory
arenas), how does vector ensure that all of the contained lists use
*its* allocator; i.e.: that all of the contained objects allocate memory
from the same arena?

As I see it, if all instances of a given allocator type are not required
to compare equal, then vector must be able to handle three different
cases:

1. The contained elements use no dynamic memory (e.g. vector< int >).
In this case, it must create them using the normal copy constructor.

2. The contained elements use dynamic memory, and use the vector
specific allocator class to allocate it (e.g. list< int , MyAlloc >).
In this case, vector must pass its instance of the allocator, as well as
the initial value, when creating contained elements.

3. The contained elements use dynamic memory, but do not use the vector
specific allocator class to allocate it.  In this case, nothing is going
to work.

|> Some of the confusion is the STL usage of empty objects.  A
|> number of classes in the STL (and elsewhere) do not instance
|> any storage.  By default, allocators are one of these.  That
|> does NOT mean they are not objects.  They will have an offset
|> in the object structure even if they have no extent.  (This
|> means that a compiler where a sloppy implementer never
|> considered the case of empty objects, might just blow up
|> when it runs across one of these 'anomolies'.)  They
|> may also have an impact on the alignment of other objects.

In cases like traits, however, the empty type is only used to
instantiate the template.  There is no instance of it in the
instantiated object.  Following your argument, the allocator types would
fall into this category.

|> On th original topic:  One of the more obtuse parts of the
|> implementation of templates is their interaction with override
|> selection.  Frankly, I don't know exactly how it works.
|> My understanding of the specs is that failure to get an exact
|> type match on the paramters for an function or operator
|> triggers a search for suitable conversion routine by the
|> compiler, including the possible generation of such a routine
|> by the template facility.  (If it generates a diagnostic
|> instead, the user is most likely to stick in an appropriate
|> cast.  In either case, the search and template instentation
|> gets done eventually.)  That means that a call to a conversion
|> routine will be generated (one way or another) by the compiler
|> if the types do not match.  I contend that these conversion
|> routines are the correct place to make deep or shallow copy
|> decisions.  Allowing for a run time test on allocators moves
|> these decisions out of the domain of the conversion routines
|> into the main stream of algorithm design and user code.

The problem is that these conversion routines must exist if they are to
be called by the compiler.  For the moment, they don't, although it
would not be difficult to imagine that all containers had a constructor:

 template< class Other >
      container( Other const& o )
 {
  initialize( o.begin() , o.end() ) ;
 }

This would mean that any container following the STL conventions could
be used to initialize any other.  (Would this cause any ambiguity
problems with the copy constructor?)
--
James Kanze         Tel.: (+33) 88 14 49 00        email: kanze@gabi-soft.fr
GABI Software, Sarl., 8 rue des Francs-Bourgeois, F-67000 Strasbourg, France
Conseils,    tudes et r   alisations en logiciel orient    objet --
                -- A la recherche d'une activit    dans une region francophone
---
[ comp.std.c++ is moderated.  To submit articles: Try just posting with your
                newsreader.  If that fails, use mailto:std-c++@ncar.ucar.edu
  comp.std.c++ FAQ: http://reality.sgi.com/austern/std-c++/faq.html
  Moderation policy: http://reality.sgi.com/austern/std-c++/policy.html
  Comments? mailto:std-c++-request@ncar.ucar.edu
]