using nested types for custom allocator of incomplete type

Discussion in 'C++' started by abir, Jun 18, 2010.

  1. abir

    abir Guest

    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<typename T>
    struct my_alloc : private select<is_small<T> , impl1<T> , impl2<T>
    >::type

    {
    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
     
    abir, Jun 18, 2010
    #1
    1. Advertising

  2. * 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{};
    >
    > template<typename T>
    > struct my_alloc : private select<is_small<T> , impl1<T> , impl2<T>
    > > ::type

    > {
    > 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

    --
    blog at <url: http://alfps.wordpress.com>
     
    Alf P. Steinbach, Jun 18, 2010
    #2
    1. Advertising

  3. abir

    abir Guest

    On Jun 18, 6:28 pm, "Alf P. Steinbach" <> wrote:
    > * 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 "<".
    >

    doesn't matter. My criterion for is_small may be different from yours.
    >
    >
    > > template<typename T>  struct impl1{};
    > > template<typename T>  struct impl2{};

    >
    > > template<typename T>
    > > struct my_alloc : private select<is_small<T>  , impl1<T>  , impl2<T>
    > > > ::type

    > > {
    > >    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?
    >

    typedef my_alloc<foo>::pointer foo_ptr;
    The above statement is not allowed, as foo is incomplete type.

    > 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.
    > > 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?
    >

    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.

    > > 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.

    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.
    >
    > > 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.
    >

    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.
    > > I need the pointers to be defined for incomplete type for any
    > > allocator, as otherwise many cyclic dependency will happen.

    >
    > Cheers & hth.,
    >
    > - Alf
    >
    > --

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

    Thanks
     
    abir, Jun 18, 2010
    #3
  4. abir wrote:
    > 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.
     
    Vladimir Jovic, Jun 18, 2010
    #4
  5. abir

    abir Guest

    On Jun 18, 8:40 pm, Vladimir Jovic <> wrote:
    > abir wrote:
    > > 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.


    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
     
    abir, Jun 18, 2010
    #5
  6. abir

    abir Guest

    On Jun 19, 12:42 am, Paul Bibbings <>
    wrote:
    > abir wrote:
    > > 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<typename T>
    > > struct my_alloc : private select<is_small<T> , impl1<T> , impl2<T>
    > >> ::type

    > > {
    > >   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.

    >
    > 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
    > Regards
    >
    > Paul Bibbings
     
    abir, Jun 19, 2010
    #6
    1. Advertising

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

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. gk245
    Replies:
    2
    Views:
    1,264
    Christopher Benson-Manica
    May 6, 2006
  2. Robert Frunzke

    custom allocator using templates

    Robert Frunzke, Jan 29, 2006, in forum: C++
    Replies:
    4
    Views:
    473
    Robert Frunzke
    Jan 29, 2006
  3. Belebele
    Replies:
    5
    Views:
    533
    Victor Bazarov
    Aug 29, 2007
  4. Replies:
    1
    Views:
    994
    Richard Bos
    Jan 17, 2008
  5. Frank Bergemann
    Replies:
    2
    Views:
    6,467
    Frank Bergemann
    Jun 27, 2009
Loading...

Share This Page