-3

I am beginning to feel out place, if in order to compile some simple code I must make 4 questions to the respectfull audience. But it seems that the custom Allocator argument is paramount difficult, according to what I experience. I have defined a custom allocator very very simple. I have a typedef Char that may be char,char16_t,char32_t .

I defined a String that is an instantiation of basic_string. I defined instances of that Class and the code correctly allocates them on the custom memory ( array named m - you may see it with the debugger). The code works correctly with various sizes. By reading the code of basic_string and by seeing the memory arrangement, it is understood that the instances of basic_strings are just pointers to the allocated memory. I defined too Vector as: vector<Char>. The problem is that sometimes the vector saves on custom memory and some times doesn't. Is it a bug or I don't understand the basic concepts ?

Vector vv={U'a',U'b',U'c',U'd',U'e',U'f',U'g',U'h'}; // doesn't assigns nothing 
vv[0]=U'a'; // works correctly
vv[1]=U'b'; // works correctly 

for(size_t i=0;vv0[i];i++) 
    vv.push_back(vv0[i]); // doesn't assign nothing.

The whole code is bellow:

//#include <bits/c++config.h>
//#define _GLIBCXX_FULLY_DYNAMIC_STRING 1
//#include <stdint.h> //#include <stddef.h> //#include <memory>
#include <iostream>
#include <string>
#include <limits>
#include <vector>

#define CONCAT_PURE(a,b,c) a ## b ## c
#define CONCAT(a,b,c) CONCAT_PURE(a,b,c)

typedef char char8_t; typedef char char64_t; // just for symmetry, char64 may exist when tokens are 64bit.

#define charSz 32 // may be 8,16,32,64 change by hand !
typedef CONCAT(char,charSz,_t) Char; // may be char8_t or char16_t or char32_t or char64_t

// the macro STR(xxxxx) permits us to write strings with characters of 8 bit 16 bit 32 bit (later on 64 bits)
#if charSz == 8
    #define STR(s) #s
#elif charSz == 16
    #define STR(s) u ## #s
#elif charSz == 32
    #define STR(s) U ## #s
#elif charSz == 64
    #define STR(s) UU ## #s
#endif 

typedef int32_t Token;typedef unsigned char byte;

typedef size_t addr;addr freePos=0;Token freeT=0;
const size_t heapSize=0x400;byte m[heapSize];

addr Allocate(size_t sz){addr t=freePos;freePos+=sz;return t;} // justs increasing
void Deallocate(addr p,size_t sz){/**((size_t*)(m+p))=sz;*/} // not important actually

template <typename T>
struct Allocator {
        // http://www.codeproject.com/Articles/4795/C-Standard-Allocator-An-Introduction-and-Implement

typedef T value_type;
typedef T* pointer;             typedef const T* const_pointer;
typedef value_type& reference;  typedef const value_type& const_reference;
typedef std::size_t size_type; typedef std::ptrdiff_t difference_type;

template<typename U> struct rebind{typedef Allocator<U> other;};

        // The initialization of freePos and freeT is done independently of the class definition because doing 
        // it in the class creator another instance (with other template parameter would reinitialize them.
    Allocator() {}
    ~Allocator() {}
    template<typename U> Allocator(U){}

    static inline pointer allocate(size_type n){return (pointer)(m+Allocate(n*sizeof(T)));}
    static void deallocate(pointer p, size_type n){Deallocate((byte*)p-m,sizeof(T)*n);}

    inline size_type max_size() const{return std::numeric_limits<size_type>::max() / sizeof(T);}
    inline void construct(pointer p, const T& t) {}
    inline void destroy(pointer p) {}
};

template <typename T>
    bool operator==(Allocator<T> const &, Allocator<T> const &) { return true; }
template <typename T>
    bool operator!=(Allocator<T> const &, Allocator<T> const &) { return false; }

typedef std::basic_string< Char,std::char_traits<Char>,Allocator<Char> > String;
typedef std::vector< Char,Allocator<Char> > Vector;

int main(){
  // fill memory with values in order to be able to see modifications
    for (size_t i=0;i<sizeof(m);i++) m[i]=0xDD; 

  String s;
  String t;
  String u;

    s=STR(nice);
    t=STR(very beautyfull);
    u=STR(good);


    Char vv0[]=U"looking_well";
    Vector vv={U'a',U'b',U'c',U'd',U'e',U'f',U'g',U'h'};
    vv[0]=U'a';
    vv[1]=U'b';

    for(size_t i=0;vv0[i];i++) 
        vv.push_back(vv0[i]);

    return 0;
}
ecatmur
  • 152,476
  • 27
  • 293
  • 366
George Kourtis
  • 2,381
  • 3
  • 18
  • 28
  • 1
    Can you add some traces so that we can see the expected and the actual results/side-effects? Note that `std::string` typically uses a *small string optimization*, where for short strings, the allocator is not used. – dyp Jul 04 '14 at 17:28
  • @dyp I explained it at the beginning. When you write: Vector vv={U'a',U'b',U'c',... } you expect that the above values will go in the Vector but they don't . The memory pointers are handled correctly but nothing gets written there. Instead vv[0]=U'a' works correclty and vv[1]=U'b' works correctly too. Instead vv.push_back handles correctly the allocation and the pointers but doesn't writes nothing to the memory. If you need something more detailed tell me exactly what to do. – George Kourtis Jul 04 '14 at 17:33
  • Your `construct` doesn't construct, it does nothing. It follows that initialization of vector elements fails. – Casey Jul 04 '14 at 17:37
  • @dyp Typically? `g++` and `clang` don't seem to implement it, since `sizeof(std::string)` is the same as `sizeof(char *)`. – user4815162342 Jul 04 '14 at 17:39
  • This works as intended for me, provided you implement `construct`. – Kerrek SB Jul 04 '14 at 17:40
  • @user4815162342 This is a property of the Standard Library implementation, and libstdc++'s `std::string` implementation is known to be non-compliant (uses COW iirc). – dyp Jul 04 '14 at 17:44
  • @GeorgeKourtis Something like `Vector vv = { U'a' }; std::cout << vv[0]; // expected: 'a'` would have been sufficient for me. – dyp Jul 04 '14 at 17:46
  • @dyp Care to provide a reference for the non-compliance? BTW aren't COW and, for that matter, SSO implementation details? – user4815162342 Jul 04 '14 at 17:47
  • @user4815162342 Yes, but COW is forbidden by the guarantees required in the Standard. Quick google search: http://stackoverflow.com/q/21431633/420683 – dyp Jul 04 '14 at 17:48
  • @dyp Ah, so C++11 killed it off. Thanks for the reference. – user4815162342 Jul 04 '14 at 17:54

2 Answers2

2

Your construct doesn't construct, it does nothing. It follows that initialization of vector elements fails. You need

::new(pointer) T(t);

in there somewhere.

Our replace it with a C++11 style:

template <class U, class... Args>
void construct(U* ptr, Args&&... args) {
  ::new(ptr) U(std:: forward<Args>(args)...);
}

Also, your destroy does not destroy. It needs p->~T();.

Casey
  • 41,449
  • 7
  • 95
  • 125
0

Thanks for the hints and the solution.

What I hadn't understood is that "construct" should be used to "construct" things when these things aren't merely just POD (plain old data) that need only some copy. ( please correct if I am wrong ).

So I THINK the correct solution to the problem is to comment out "construct" because I just need the default behaviour. I did it and it works as expected.

But anyway there is some difference between basic_string and vector but because it works for basic_string ( that means that basic string ignores construct )

George Kourtis
  • 2,381
  • 3
  • 18
  • 28
  • See: http://stackoverflow.com/questions/22487267/unable-to-use-custom-allocator-with-allocate-shared-make-shared and.. http://stackoverflow.com/questions/21812175/boost-pool-experience-requested-is-it-useful-as-allocator-with-preallocation Both work with `vector` and `string` and neither have `construct` or `destroy`. Thanks to `allocator_traits`, it will use the default implementation for the missing functions. – Brandon Jul 05 '14 at 00:24