Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Concept for location

48 views
Skip to first unread message

stayp...@gmail.com

unread,
Jul 18, 2018, 9:14:13 AM7/18/18
to

I would like to create a class/template but want to prevent at compile time where it can live. Seems like type_traits or the upcoming concepts cannot be use for that.

I have two scenarios:
- Enforce at compile time that an object can only be instanciate on the stack. This is to implement a "safe" dynarray type like object.
- Enforce at compile tiem that an object can only be instanciate on the heap. This is to ensure That no "Big" object are instanciated on the stack.

I guess being able to enforce/prevent being global could be usefull. Example an object you don't want global because its constructure depends on some stuff initialised in the main().

Does this idea make any kind of sense?

I can see it being complicated to implement with scenarios that might be impossible detect at compile time. Maybe it's a feature more suited to a static analyser?

Regards.

Adam Wysocki

unread,
Jul 19, 2018, 4:09:19 AM7/19/18
to
stayp...@gmail.com wrote:

> - Enforce at compile time that an object can only be instanciate on the stack. This is to implement a "safe" dynarray type like object.

No idea here.

> - Enforce at compile tiem that an object can only be instanciate on the heap. This is to ensure That no "Big" object are instanciated on the stack.

The only solution I see is creating a "wrapper" class that would
instantiate the desired class on the heap even if the wrapper is
instantiated on the stack.

--
[ Adam Wysocki :: Warsaw, Poland ]
[ Email: a@b a=grp b=chmurka.net ]
[ Web: http://www.chmurka.net/ ]

Paavo Helde

unread,
Jul 19, 2018, 9:11:57 AM7/19/18
to
On 18.07.2018 16:14, stayp...@gmail.com wrote:
>
> I would like to create a class/template but want to prevent at compile time where it can live. Seems like type_traits or the upcoming concepts cannot be use for that.

This is against the general idea of modularity. The idea is to provide
building blocks which can be combined flexibly by the users of those
building blocks. Restricting the object placement without a very good
reason limits this flexibility and should be enforced by project
guidelines/policies, not by the software libraries themselves.

> I have two scenarios:
> - Enforce at compile time that an object can only be instanciate on the stack. This is to implement a "safe" dynarray type like object.

What do you mean by "safe dynarray"? What is "unsafe dynarray"?

One option is to define operator new and operator new[] for the class as
=delete. However, the client code can easily circumvent this by placing
the class inside a wrapper.

Anyway, I do not see a reason why something which can be placed on the
stack should not be allocated dynamically. In some very rare
circumstances one might even want to allocate a mutex lock dynamically.

> - Enforce at compile tiem that an object can only be instanciate on the heap. This is to ensure That no "Big" object are instanciated on the stack.

I have no idea what could be such a Big object so that its size is huge
and known at compile time already. A table of possible Rubic cube
states? Seems more like a non-existing issue.

Enforcing on heap might be useful for polymorphic types which are
supposed to be accessed by pointers only. But then the issue already
mostly resolves itself, if the client code wants to use an interface
taking a smartpointer to object, it first needs to have one, and for
that it needs to construct the object dynamically, for example by
std::make_unique().

This can even be enforced a bit by defining a private destructor:

#include <memory>

class A {
private:
~A() {}
friend struct std::default_delete<A>;
public:
// ...
};

An A can now be created with std::make_unique().

For std::make_shared it seems to become more complicated unfortunately
and I haven't found a good solution, other than providing a custom
smartpointer class which does not allow creation from a raw pointer, and
providing static factory methods for creating the objects dynamically
only, as suggested by other posts.

> I guess being able to enforce/prevent being global could be usefull. Example an object you don't want global because its constructure depends on some stuff initialised in the main().

Mutable globals are indeed evil and should be avoided. In this example
it seems quite easy to achieve, the object constructor should just take
"the stuff initialized in the main()" as a parameter (and of course the
"stuff" itself should be only created when initialized, i.e. not a global).

>
> Does this idea make any kind of sense?

Not much, to be honest. I am even doubting my own long-winded efforts to
suppress stack initialization.

stayp...@gmail.com

unread,
Jul 19, 2018, 8:47:05 PM7/19/18
to
Le jeudi 19 juillet 2018 09:11:57 UTC-4, Paavo Helde a écrit :

> >
> > I would like to create a class/template but want to prevent at compile time where it can live. Seems like type_traits or the upcoming concepts cannot be use for that.
>
> This is against the general idea of modularity. The idea is to provide
> building blocks which can be combined flexibly by the users of those
> building blocks. Restricting the object placement without a very good
> reason limits this flexibility and should be enforced by project
> guidelines/policies, not by the software libraries themselves.

Point taken

>
> > I have two scenarios:
> > - Enforce at compile time that an object can only be instanciate on the stack. This is to implement a "safe" dynarray type like object.
>
> What do you mean by "safe dynarray"? What is "unsafe dynarray"?

dynarray was a proposed implementation of dynamic array, was voted in working paper in 2013, it was removed from C++14 later in the same year. Definitely cancel in March 2016. For more details: http://open-std.org/JTC1/SC22/WG21/docs/papers/2017/p0785r0.html

>
> One option is to define operator new and operator new[] for the class as
> =delete. However, the client code can easily circumvent this by placing
> the class inside a wrapper.
>
> Anyway, I do not see a reason why something which can be placed on the
> stack should not be allocated dynamically. In some very rare
> circumstances one might even want to allocate a mutex lock dynamically.
>

dynarray is the motivation I have in mind.

> > - Enforce at compile tiem that an object can only be instanciate on the heap. This is to ensure That no "Big" object are instanciated on the stack.
>
> I have no idea what could be such a Big object so that its size is huge
> and known at compile time already. A table of possible Rubic cube
> states? Seems more like a non-existing issue.

The OS I'm working with has a defaults stack size of 128K. Some embedded system have less. That being said I understand that it would not be a way to safely control stack usage at compile time, but still. I've often seen issues with something like std::vector::resize where a temporary is created to be copied onto the newly allocated slots. This temporary was busting the stack.
Thanks.

Alf P. Steinbach

unread,
Jul 19, 2018, 11:06:22 PM7/19/18
to
On 18.07.2018 15:14, stayp...@gmail.com wrote:
>
> I would like to create a class/template but want to prevent at
> compile time where it can live. Seems like type_traits or the
> upcoming concepts cannot be use for that.
>
> I have two scenarios:
> - Enforce at compile time that an object can only be instanciate on
> the stack. This is to implement a "safe" dynarray type like object.

Since you mention elsewhere that the /purpose/ is to use stack
allocation like a VLA or alloca, there is as far as I know no portable
way to differentiate whether an object is allocated in automatic memory,
as a static variable, or as part of an array or class type object that
in turn can be allocated anywhere.

Semi-portably you might compare the object's address to an address
obtained in `main`, assuming that the stack has some maximum size (that
you might infer from a preprocessor symbol) and that it grows down in
memory (again probably best to let the build commands specify).

That approach gives you runtime checking.

As a convenience measure, getting some ungood usage errors up front at
compile time, I would then "= delete" the class' allocation functions,
`operator new` and `operator new[]`.


> - Enforce at compile tiem that an object can only be instanciate on
> the heap. This is to ensure That no "Big" object are instanciated > on the stack.

I would make the destructor non-public, i.e. implement the restriction
with one or two lines:

protected:
virtual ~Dynamic_only() {}

That works nicely with e.g. `std::make_shared`, at the cost of having to
use a simple wrapper function. It doesn't work quite so nicely with
`std::make_unique`, requiring per-class boilerplate code. But doable, as
exemplified below.

The `std::make_shared` support below, the `std::make_shared` wrapper,
can easily be moved to a reusable mix-in class, I think.

Note: the FAQ instead recommends making the constructors non-public and
defining factory functions. In C++03 that wasn't so practical, requiring
one factory function per constructor. And so I argued a bit with
Marshall Cline (the FAQ maintainer at the time), to change the FAQ to
recommend the non-public destructor approach, but to no avail.

However, with C++11 we now have ~perfect argument forwarding, so that
the umpteen factory functions of C++03 can be rolled into one per class,
so that the FAQ's advice isn't so impractical anymore. :) So that's an
alternative. However I like being able to use the usual instantiation
code instead of calling factory functions.


----------------------------------------------------------------------
#include <memory> // std::make_shared
#include <type_traits> // std::is_base_
#include <utility> // std::(enable_if_t, forward)
using namespace std;

template< class T > using ptr_ = T*;
template< class T > using ref_ = T&;

class Dynamic_only
{
protected:
virtual ~Dynamic_only() {}

public:
Dynamic_only() {}
Dynamic_only( const int, const int, const int ) {}

// A default unique_ptr doesn't store a unique deleter, so one must
// use indirection via a specialization of std::default_delete. This
// works for direct use of this class, but a derived class needs to
// duplicate the effort. std::unique_ptr is just deleter-challenged,
// which is a cost of zero overhead for the default instantiation.
static void destroy( ptr_<Dynamic_only> p ) { delete p; }

// A default shared_ptr stores a unique deleter obtained at the point
// where the shared_ptr is constructed, e.g. in the return stm below:
template< class... Args >
static auto make_shared( Args&&... args )
-> shared_ptr<Dynamic_only>
{
struct Accessible: Dynamic_only
{
using Dynamic_only::Dynamic_only; // Constructors.
~Accessible() override {}; // Just to be explicit.
};

return std::make_shared<Accessible>( forward<Args>( args )... );
}
};

#ifndef TEST_UNSUPPORTED_MAKE_UNIQUE
// Annoyingly verbose support for std::unique_ptr:
namespace std
{
template<>
struct default_delete<Dynamic_only>
{
constexpr default_delete()
noexcept
{}

template< class U
, class = enable_if_t< is_convertible_v< ptr_< U >, ptr_<
Dynamic_only > > >
>
default_delete( ref_<const default_delete<U>> )
noexcept
{}

void operator()( const ptr_<Dynamic_only> p ) const
{
Dynamic_only::destroy( p );
}
};
} // namespace std
#endif

auto main()
-> int
{
const auto up1 = make_unique<Dynamic_only>();
const auto up2 = make_unique<Dynamic_only>( 1, 2, 3 );

const auto sp1 = Dynamic_only::make_shared();
const auto sp2 = Dynamic_only::make_shared( 1, 2, 3 );

(void) up1, up2, sp1, sp2;

#ifdef TEST_STACK_ALLOC
Dynamic_only o; //! Doesn't
compile.
(void) o;
#endif

#ifdef TEST_UNSUPPORTED_MAKE_UNIQUE
const auto up = std::make_unique<Dynamic_only>(); //! Doesn't
compile.
(void) up;
#endif

#ifdef TEST_STD_MAKE_SHARED
const auto sp = std::make_shared<Dynamic_only>(); //! Doesn't
compile.
(void) sp;
#endif
}
----------------------------------------------------------------------


> I guess being able to enforce/prevent being global could be usefull. Example an
> object you don't want global because its constructure depends on some stuff
> initialised in the main().
>
> Does this idea make any kind of sense?

Yes, but it's an example of an issue that perhaps is better solved with
just a code /comment/.

Less work. :)


> I can see it being complicated to implement with scenarios that
> might be impossible detect at compile time. Maybe it's a feature more suited
> to a static analyser?

Not sure. Maybe.


Cheers!,

- Alf

Soviet_Mario

unread,
Jul 20, 2018, 9:13:07 AM7/20/18
to
Il 18/07/2018 15:14, stayp...@gmail.com ha scritto:
>
> I would like to create a class/template but want to prevent at compile time where it can live. Seems like type_traits or the upcoming concepts cannot be use for that.
>
> I have two scenarios:
> - Enforce at compile time that an object can only be instanciate on the stack. This is to implement a "safe" dynarray type like object.
> - Enforce at compile tiem that an object can only be instanciate on the heap. This is to ensure That no "Big" object are instanciated on the stack.
>
> I guess being able to enforce/prevent being global could be usefull. Example an object you don't want global because its constructure depends on some stuff initialised in the main().
>
> Does this idea make any kind of sense?

Imvho, not much, but this I deem has nothing to do with
LOCATION but rather with PERSISTENCE, scope and lifetime
control of objects.
Were it only for the position, it could be very acceptable
some limitation.
But compelling to live on stack eliminates every possible
control over lifetime of the object, at least in traditional
procedural paradigm.
I don't know "functional" paradigm enough, but perhaps in
that scenario it would be different ... and perhaps the
question itself would become meaningless there.

In procedural style, you still can control a bit the
lifetime, but only "bubbling out of the functions the
objects, moving them from local to formal arguments, leading
to unneeded pollution of parameters to extend the lifetime
to outer callers. Everything created in the callee dies in
it. It's a limitation, don't you think ?

If you need the intrinsic "reuse" feature of the stack, you
could think about external stack containers, software stacks
I mean.

>
> I can see it being complicated to implement with scenarios that might be impossible detect at compile time. Maybe it's a feature more suited to a static analyser?
>
> Regards.
>


--
1) Resistere, resistere, resistere.
2) Se tutti pagano le tasse, le tasse le pagano tutti
Soviet_Mario - (aka Gatto_Vizzato)

Jorgen Grahn

unread,
Jul 24, 2018, 4:29:40 PM7/24/18
to
On Wed, 2018-07-18, stayp...@gmail.com wrote:
>
> I would like to create a class/template but want to prevent at compile time where it can live. Seems like type_traits or the upcoming concepts cannot be use for that.
>
> I have two scenarios:

> - Enforce at compile time that an object can only be instanciate
> - on the stack. This is to implement a "safe" dynarray type like
> - object.

Misfeldt et al (2004): Elements of C++ Style, item 102:

class Foo {
private:
void* operator new(size_t);
void* operator new[](size_t);
};

I suppose nowadays you'd say "... = delete".

> - Enforce at compile tiem that an object can only be instanciate
> - on the heap. This is to ensure That no "Big" object are
> - instanciated on the stack.

Item 103:

class Foo {
private:
friend class std::auto_ptr<Foo>;
~Foo();
};

I haven't tried either of these, but I'm generally for removing
features you don't plan to use.

/Jorgen

> I guess being able to enforce/prevent being global could be
> usefull. Example an object you don't want global because its
> constructure depends on some stuff initialised in the main().

>
> Does this idea make any kind of sense?
>
> I can see it being complicated to implement with scenarios that
> might be impossible detect at compile time. Maybe it's a feature
> more suited to a static analyser?

--
// Jorgen Grahn <grahn@ Oo o. . .
\X/ snipabacken.se> O o .
0 new messages