2005년 5월 24일 화요일

STL custom allocator

STL에 기본 할당자 대신 자신이 만든 것 이용하기
Effective STL ch.10, 11

예)
// SharedMemory Allocator
#include <map>
#include <string>
#include <vector>
#include <sys/types.h>
#include <sys/wait.h>
#include "mm.h"

void *mallocShared(size_t bytes)
{
    return MM_malloc(bytes);
}

void freeShared(void *p)
{
    MM_free(p);
}

template<typename T>
class SharedMemoryAllocator
{
    public:
        typedef size_t size_type;
        typedef ptrdiff_t difference_type;
        typedef T* pointer;
        typedef const T * const_pointer;
        typedef T& reference;
        typedef const T * const_reference;
        typedef T value_type;

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

        SharedMemoryAllocator() throw() { }
        SharedMemoryAllocator(const SharedMemoryAllocator&) throw() { }

        template<typename U>
            SharedMemoryAllocator(const SharedMemoryAllocator<U>&) throw() { }

        ~SharedMemoryAllocator() throw() { }

        pointer address(reference __x) const { return &__x; }
        const_pointer address(const_reference __x) const { return &__x; }

        pointer allocate(size_type numObjects, const void *localityHint = 0)
        {
            return static_cast<pointer>(mallocShared(numObjects * sizeof(T)));
        }

        void deallocate(pointer ptrToMemory, size_type numObjects)
        {
            freeShared(ptrToMemory);
        }

        size_type max_size() const throw()
        {
            return size_t(-1) / sizeof(T);
        }

        // _GLIBCXX_RESOLVE_LIB_DEFECTS
        // 402. wrong new expression in [some_] allocator::construct
        void construct(pointer __p, const T& __val)
        {
            new(__p) T(__val);  // placement new
        }

        void destroy(pointer __p)
        {
            __p->~T();
        }
};

template<typename T>
inline bool operator==(const SharedMemoryAllocator<T>&, const SharedMemoryAllocator<T>&)
{
    return true; // 왜 항상 true일까?
}

template<typename T>
inline bool operator!=(const SharedMemoryAllocator<T>&, const SharedMemoryAllocator<T>&)
{
    return false; // 왜 항상 false일까?
}

//! createOnShared로 만든 type은 꼭 destructor를 명시적으로 불러주어야 한다.
template<typename T>
T *createOnShared()
{
    void *chunk_p = mallocShared(sizeof(T)); // memory chunk
    if (chunk_p == NULL)
        return NULL;
    T *shm_p = new(chunk_p) T; // placement new(new가 실제로 메모리를 잡지는 않은)
    return shm_p;
}

template<typename T>
void releaseOnShared(T *shm_p)
{
    shm_p->~T();
    void *chunk_p = reinterpret_cast<void*>(shm_p);
    freeShared(chunk_p);
}

이용)
typedef std::vector<int, SharedMemoryAllocator<int> > SharedIntVector;
MM_create(100000000, "testtest");
SharedIntVector *plv = createOnShared<SharedIntVector>();
plv->push_back(1);
printf("MM_available : %d\n", MM_available());
releaseOnShared<SharedIntVector>(plv);

이용2)
// 참고
// /usr/include/c++/3.2.2/string
// /usr/include/c++/3.2.2/bits/stringfwd.h
    typedef std::basic_string<char, std::char_traits<char>, SharedMemoryAllocator<char> > SharedStr;

    typedef std::vector<SharedStr, SharedMemoryAllocator<SharedStr> > SharedStrVec;
// /usr/include/c++/3.2.2/map
// /usr/include/c++/3.2.2/bits/stl_map.h
    typedef std::map<SharedStr, SharedStr, less<SharedStr>, SharedMemoryAllocator<SharedStr> > SharedStrMap;

    void *pChunk1;
    pChunk1 = shAlloc1.malloc(sizeof(SharedStrVec));
    SharedStrVec *shVecStr1 = new(pChunk1) SharedStrVec;

    void *pChunk2;
    pChunk2 = shAlloc1.malloc(sizeof(SharedStrMap));
    SharedStrMap *map = new(pChunk2) SharedStrMap;
    shVecStr1->push_back("1");
    shVecStr1->push_back("2");
    shVecStr1->push_back("3");
    map->insert(SharedStrMap::value_type("1", "1"));
    map->insert(SharedStrMap::value_type("2", "2"));
    map->insert(SharedStrMap::value_type("3", "3"));

    pid_t p = fork();

    if (p == 0) {
        // child
        printf("child start\n");
        shVecStr1->push_back("4");
        shVecStr1->push_back("5");
        shVecStr1->push_back("6");
        map->insert(SharedStrMap::value_type("4", "4"));
        map->insert(SharedStrMap::value_type("5", "5"));
        map->insert(SharedStrMap::value_type("6", "6"));
        printf("child end\n");
        exit(0);
    } else if (p > 0) {
        // parent
        sleep(3);
        waitpid(p, NULL, 0);
        printf("parent start\n");
        shVecStr1->push_back("7");
        shVecStr1->push_back("8");
        shVecStr1->push_back("9");
        map->insert(SharedStrMap::value_type("7", "7"));
        map->insert(SharedStrMap::value_type("8", "8"));
        map->insert(SharedStrMap::value_type("9", "9"));

        SharedStrVec::iterator it;
        for (it = shVecStr1->begin(); it != shVecStr1->end(); it++) {
            printf("%s\n", it->c_str());
        }

        SharedStrMap::iterator it2;
        for (it2 = map->begin(); it2 != map->end(); it2++) {
            printf("%s -> %s \n", it2->first.c_str(), it2->second.c_str());
        }

        printf("parent end\n");
    } else {
        // error
        fprintf(stderr, "fork() failed.\n");
    }

    shVecStr1->~SharedStrVec();
    shAlloc1.free(pChunk1);
    map->~SharedStrMap();
    shAlloc1.free(pChunk2);

    return 0;
}

placement new

이미 메모리가 할당되어 있을 때,
그 메모리에 object를 넣고 생성자를 불러줌.
(일반적인 new와는 달리 메모리를 할당하는 일은 하지 않는 다.)

소멸자도 수동으로 불러줘야 하고
메모리 해제도 raw memory에 대해서 해줘야 한다.

참고)
More Effective C++, Item 4, 8
Linux에서 gcc 3.4를 설치한 후 /usr/include/c++/3.4/ext/new_allocator.h

댓글 없음:

댓글 쓰기