using nested types for custom allocator of incomplete type

A

abir

I have an custom allocator implementation whose base type depends on
completeness of the type parameter T, though the nested types doesn't
depend on so.

e.g.

template<typename T>
struct is_Small
{
const static bool value = (sizeof(T) < sizeof(void*) );
};

template<typename T> struct impl1{};
template<typename T> struct impl2{};

template said:
{
typedef T* pointer;
...
};

struct foo; //incomplete type.
typedef my_alloc<foo>::pointer foo_ptr;

The same problem are supposed to happen for stack_alloc<T,N> also, as
usually they has std::aligned_storage as memory.

So far with std::allocator these wasn't a problem though it's allocate
function is usually
return operator new (sizeof(T) *n);

I can pull the traits in a separate class as
template<typename T>
struct alloc_traits
{
typedef. T* pointer ; ...
};

and use it as
typedef alloc_traits<foo>::pointer foo_ptr;

But then I need to change the code everywhere.

Is it possible to design my_alloc such as the nested types which are
independent can still work where rest of functionality depends on
completeness of T ?
Or even is it possible to define some lazy_typedef which don't try to
instantiate the class.
I need the pointers to be defined for incomplete type for any
allocator, as otherwise many cyclic dependency will happen.

Thanks.
abir
 
A

Alf P. Steinbach

* abir, on 18.06.2010 14:00:
I have an custom allocator implementation whose base type depends on
completeness of the type parameter T, though the nested types doesn't
depend on so.

e.g.

template<typename T>
struct is_Small
{
const static bool value = (sizeof(T)< sizeof(void*) );
};

Probably you mean "<=" here, not "<".

template<typename T> struct impl1{};
template<typename T> struct impl2{};


{
typedef T* pointer;
...
};

struct foo; //incomplete type.
typedef my_alloc<foo>::pointer foo_ptr;

The same problem are supposed to happen for stack_alloc<T,N> also, as
usually they has std::aligned_storage as memory.

What is the problem?

You can not determine the size of an incomplete type and it makes no sense to
try to allocate storage to hold an instance of such a type.

Also, what is 'stack_alloc<T, N>'?


So far with std::allocator these wasn't a problem though it's allocate
function is usually
return operator new (sizeof(T) *n);

I can pull the traits in a separate class as
template<typename T>
struct alloc_traits
{
typedef T* pointer ; ...
};

and use it as
typedef alloc_traits<foo>::pointer foo_ptr;

But then I need to change the code everywhere.

Why would you do that?

Is it possible to design my_alloc such as the nested types which are
independent can still work where rest of functionality depends on
completeness of T ?

Please provide a complete example exhibiting the problem.

Copy and paste the code, do not retype it manually.

The FAQ item about how to post a question about Code That Does Not Work provides
some good advice for formulating your question in a way so that it might be
answered.

Or even is it possible to define some lazy_typedef which don't try to
instantiate the class.

You can always specialize your 'is_Small' for any type, even an incomplete one.

But it makes no sense to try to allocate storage for an instance of an
incomplete type.

I need the pointers to be defined for incomplete type for any
allocator, as otherwise many cyclic dependency will happen.


Cheers & hth.,

- Alf
 
A

abir

* abir, on 18.06.2010 14:00:


Probably you mean "<=" here, not "<".
doesn't matter. My criterion for is_small may be different from yours.
What is the problem?
You can not determine the size of an incomplete type and it makes no sense to
try to allocate storage to hold an instance of such a type.
Where am I allocating storage for an incomplete type?
I am only interested to have to have the above typedef which doesn't
work at present
as the base class selection for my_alloc depends on completeness of
foo.
Also, what is 'stack_alloc<T, N>'?
I am sure you know, but still for completeness,
#include <tr1/type_traits>

template<typename T,unsigned N>
struct stack_alloc
{
typedef T* pointer;
std::tr1::aligned_storage<(sizeof(T)*N),
std::tr1::alignment_of<T>::value> buffer_;
};

struct foo;
typedef stack_alloc<foo,10>::pointer foo_ptr;
The line above won't work as foo is incomplete type, though I am only
interested to typedef nested type.
Why would you do that?
See the reason above. I am interested to typedef pointer type of an
allocator of incomplete type.
When I create such an allocator object, I will surely have complete
type of foo available.
Please provide a complete example exhibiting the problem.

Copy and paste the code, do not retype it manually.
I didn't had my working PC connected to network, now I have.
The code below is NOT compilable.
template<typename T>
struct is_small
{
const static bool value = sizeof(T) <= sizeof(void*);
//i am putting <= , as sure you know what i am trying to do.
//though i use part of the pointer for other purpose, so it
//is actually < instead of <=
};

template<typename T> struct impl1{};
template<typename T> struct impl2{};

template<bool B,typename T1,typename T2>
struct select
{
typedef T1 type;
};
template<typename T1,typename T2>
struct select<false,T1,T2>
{
typedef T2 type;
};

template<typename T>
struct my_alloc : private select< is_small<T>::value,impl1<T>,
impl2<T> >
{
typedef T* pointer;
};
struct foo;
typedef my_alloc<foo>::pointer foo_ptr;
struct bar;
typedef my_alloc<bar>::pointer bar_ptr;
//and later
struct foo{};
struct bar{};
I want to make it compilable without having full definition of foo
available for typedef to foo_ptr.
And I prefer to change my_alloc in such a way keeping its
functionality same so that I won't have to change every typedef to
foo_ptr , bar_ptr.
The FAQ item about how to post a question about Code That Does Not Work provides
some good advice for formulating your question in a way so that it might be
answered.


You can always specialize your 'is_Small' for any type, even an incomplete one.
That is not what I want. I want to alias nested types of my_alloc
which doesn't depend on completeness of T.
The solution presently i have is to pull those inner types in a
separate class as alloc_traits
as shown
template<typename T>
struct alloc_traits
{
typedef T* pointer;
};
struct foo;
typedef alloc_traits<foo>::pointer foo_ptr;
struct bar;
typedef alloc_traits<bar>::pointer bar_ptr;

I do not like this solution as I have to change all of the typedefs .
Looking for a solution where I have to change only my_alloc
or a generic solution which will delay the completeness checking.
But it makes no sense to try to allocate storage for an instance of an
incomplete type.
I know. But no where i am creating storage for incomplete type. Only
interested to have a typedef to pointer nested type of an allocator
for incomplete type. And I believe pointer of an incomplete type
should be allowed all allocators including std::allocator., whatever
may be implementation is.
Cheers & hth.,

- Alf

-- Hope it clears the confusion.
blog at <url:http://alfps.wordpress.com>
Thanks
 
V

Vladimir Jovic

abir said:
The code below is NOT compilable.
template<typename T>
struct is_small
{
const static bool value = sizeof(T) <= sizeof(void*);

*********
//i am putting <= , as sure you know what i am trying to do.
//though i use part of the pointer for other purpose, so it
//is actually < instead of <=
};

template<typename T> struct impl1{};
template<typename T> struct impl2{};

template<bool B,typename T1,typename T2>
struct select
{
typedef T1 type;
};
template<typename T1,typename T2>
struct select<false,T1,T2>
{
typedef T2 type;
};

template<typename T>
struct my_alloc : private select< is_small<T>::value,impl1<T>,
impl2<T> >
{
typedef T* pointer;
};
struct foo;
typedef my_alloc<foo>::pointer foo_ptr;


Here you are requesting sizeof of an undefined class. That is not allowed.
struct bar;
typedef my_alloc<bar>::pointer bar_ptr;
//and later
struct foo{};
struct bar{};


From the standard (see 5.3.3) :

The sizeof operator yields the number of bytes in the object
representation of its operand. The operand is either an expression,
which is not evaluated, or a parenthesized type-id. The sizeof operator
shall not be applied to an expression that has function or incomplete
type, or to an enumeration type before all its enumerators have been
declared, or to the parenthesized name of such types, or to an lvalue
that designates a bit-field. sizeof(char), sizeof(signed char) and
sizeof(unsigned char) are 1; the result of sizeof applied to any other
fundamental type (3.9.1) is implementation-defined.
 
A

abir

                                 *********







Here you are requesting sizeof of an undefined class. That is not allowed..


 From the standard (see 5.3.3) :

The sizeof operator yields the number of bytes in the object
representation of its operand. The operand is either an expression,
which is not evaluated, or a parenthesized type-id. The sizeof operator
shall not be applied to an expression that has function or incomplete
type, or to an enumeration type before all its enumerators have been
declared, or to the parenthesized name of such types, or to an lvalue
that designates a bit-field. sizeof(char), sizeof(signed char) and
sizeof(unsigned char) are 1; the result of sizeof applied to any other
fundamental type (3.9.1) is implementation-defined.

I know the problem. I know that sizeof depends on complete type. I had
given two implementation to show that because of that I have some
problem with typedef to the nested type pointer. I also showed the
solution which I presently use. I am looking for a alternative good
solution which is consistent with std::allocator interface . Base
class for my_alloc depends on sizeof(T) but my_alloc<T>::pointer is
just T*. which doesn't depend on completeness of T. That is why
presently I use a alloc_traits as a separate class for the nested
typedefs which doesn't depend on completeness of T.
In general , for any allocator , such as std::allocator ,
allocator<incomplete_type>::pointer etc is allowed. I want to make
my_alloc with same interface as std::allocator and typedef
my_alloc<incomplete_type>::pointer some_ptr; should be allowed, though
internal implementation of my_alloc depends on completeness of T, and
NOT the inner type pointer.

Hope that clears about the solution presently I have, and my intention
about the interface for my_alloc I want to have.
Thanks
 
A

abir

The problem, as you have stated it, is that the type(s) that you inherit
my_alloc from require T to be a complete type.  You are looking for ways to work
around this, no doubt, but have you considered breaking this dependency?  I am
envisaging something along the lines of:

   // file: custom_allocator.cpp

   #include <cstdlib>
   #include <cstddef>

   template<typename T>
   struct allocator_traits
   {
      typedef size_t size_type;
      typedef T*     pointer;
      // ...
   };

   template<
      typename T,
      template<typename> class Alloc = allocator_traits
   >
   struct impl1
   {
      typedef typename Alloc<T>::size_type size_type;
      typedef typename Alloc<T>::pointer   pointer;
      // ...

      static pointer allocate(size_type n) {       // or whatever
         return (pointer) malloc(n * sizeof(T));
      }
      static void deallocate(void* p) {            // ditto
         if (p) free(p);
      }
      // ...
   };

   template<
      typename T,
      template<typename> class Alloc = allocator_traits
   >
   struct impl2
   {
      typedef typename Alloc<T>::size_type size_type;
      typedef typename Alloc<T>::pointer   pointer;
      // ...

      static pointer allocate(size_type n) {       // or whatever
         return (pointer) malloc(n * sizeof(T));
      }
      static void deallocate(void* p) {            // ditto
         if (p) free(p);
      }
      // ...
   };

   template<typename T>
   struct is_small
   {
      const static bool value = sizeof(T) < sizeof(void*);
   };

   template<
      bool B,
      typename T,
      typename T1 = impl1<T>,
      typename T2 = impl2<T>
   >
   struct select
   {
      typedef T1 type;
   };

   template<
      typename T,
      typename T1,
      typename T2
   >
   struct select<false, T, T1, T2>
   {
      typedef T2 type;
   };

   template<
      typename T,
      template<typename> class Alloc = allocator_traits
   >
   class my_alloc
   {
   public:
      typedef typename Alloc<T>::size_type size_type;
      typedef typename Alloc<T>::pointer   pointer;
      // ...

      pointer allocate(size_type n, const void* = 0) {
         return select<is_small<T>::value, T>::type::allocate(n);
      }
      void deallocate(void* p, size_type) {
         select<is_small<T>::value, T>::type::deallocate(p);
      }
      // ...
   };

   struct foo;      // incomplete type

   typedef my_alloc<foo>::pointer foo_ptr;   // OK

   struct foo { };

Note that, here, I have not used an allocator_traits type to *solve* your
problem, but merely as a convenience.
Nice. This is nearly what I wanted, though not exactly. Actually impl1
& impl2 contains state, otherwise I could have put all of the
implementations in the same class my_alloc. It seems there is no way
to tackle that apart from having another indirection using a base
class (non polymorphic) for impl1 & impl2 and storing that pointer in
my_alloc. And then using static dispatch.

A similar kind problem with automatically choosing between boost
shared_ptr and intrusive_ptr, given that the two construct has enough
similarity and differences are mostly how ref count is stored, may not
be possible for incomplete types, as accessing any nested typedef for
it instantiates the class.

e.g.
template<typename T>
struct refcount_ptr : select < has_add_ref<T> ,
boost::intrusive_ptr<T> , boost::shared_ptr<T> > {};

struct foo;
typedef refcount_ptr<foo> foo_ptr;
typedef foo_ptr::element_type foo_type; //should be same as foo and
doesn't depend on completeness of foo.
//the line above is problematic in same way. though element_type for
both intrusive_ptr and shared_ptr are allowed when used with
incomplete type.
and then,

struct foo{};
void use()
{
foo_ptr p (new foo());//let it resolve the complete type of foo_ptr
here.
]
I am not seeing any way to suspend the type checking process untill
complete type information is found.

Thanks for looking at the problem and giving a solution.

Regards
abir
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,015
Latest member
AmbrosePal

Latest Threads

Top