1

I need a custom Allocator to create share_pointer objects ultimately using a pool of dedicated memory. For some classes, however, I will need to further specialize the Allocators. These specialized Allocators, however, are never called.

The example defines a templeted custom allocator, and specializes the allocator for class Special.

#include <memory>

#include "stdio.h"
#include "stdint.h"
#include "string.h"

void* memAllocate(const size_t amount) {
    void* result = new char[amount];
    printf("memAllocate: %p\n", result);
    return result;
}

void memFree(void *pointer) {
    printf("memFree:     %p\n", pointer);
    delete pointer;
}

typedef struct General {
    char buffer[100];
} General;

typedef struct Special {
    char buffer[100];
} Special;

template <class T>
class Allocator
{
public:
    using value_type    = T;
    using pointer       = value_type*;

    Allocator() noexcept {}
    template <class U> Allocator(Allocator<U> const&) noexcept {}

    value_type* allocate(std::size_t n)
    {
        value_type* result = static_cast<value_type*>(memAllocate(n*sizeof(value_type)));
        printf("Allocator<generic>::allocate: %p\n", result);
        return result;
    }

    void deallocate(value_type* p, std::size_t n) noexcept
    {
        printf("Allocator<generic>::deallocate: %p\n", p);
        memFree(p);
    }
};

template <>
class Allocator<Special>
{
public:
    using value_type    = Special;
    using pointer       = value_type*;

    Allocator() noexcept {}
    template <class U> Allocator(Special const&) noexcept {}

    Special* allocate(std::size_t n)
    {
        Special* result = static_cast<Special*>(memAllocate(n*sizeof(Special)));
        printf("Allocator<Special>::allocate: %p\n", result);

        return result;
    }

    void deallocate(Special* p, std::size_t) noexcept
    {
        printf("Allocator<Special>::deallocate: %p\n", p);
        memFree(p);
    }
};

template <class T, class U>
bool operator==(Allocator<T> const&, Allocator<U> const&) noexcept
{
    return true;
}

template <class T, class U>
bool operator!=(Allocator<T> const& x, Allocator<U> const& y) noexcept
{
    return !(x == y);
}

int main(int argc, char **argv) {
    {
        Allocator<General> general;
        std::shared_ptr<General> sptr = std::allocate_shared<General>(general);
        sptr.reset();
    }

    {
        Allocator<Special> special;
        std::shared_ptr<Special> sptr = std::allocate_shared<Special>(special);
        sptr.reset();
    }

    return 0;

}

Using g++ version 12.2.0 and c++17: The output for the example code shows that while my custom allocator is called, even for the Special case the generic version is used.

// general
memAllocate: 0x55ea3b904eb0
Allocator<generic>::allocate: 0x55ea3b904eb0
Allocator<generic>::deallocate: 0x55ea3b904eb0
memFree:     0x55ea3b904eb0
// special
memAllocate: 0x55ea3b904eb0
Allocator<generic>::allocate: 0x55ea3b904eb0
Allocator<generic>::deallocate: 0x55ea3b904eb0
memFree:     0x55ea3b904eb0

What am I doing wrong? My theory is that the allocator is not called for the plain General and Special classes but rather some classes templeted therefrom.

I expect that in the general case, Allocator is used, while for class Special Allocator is used.

What type is used by std::allocate_shared to allocate memory? seems to relate to my problem though I don't understand the answers.

8
  • 1
    std::allocate_shared<T> generally doesn't allocate memory for T, but rather for some internal data structure that contains an instance of T as well as a control block. The allocator you pass is rebound to the type of that structure. Commented Dec 22, 2024 at 22:45
  • The answers to the question you cite explain your situation exactly. What about them do you find unclear? Commented Dec 22, 2024 at 22:49
  • I don't understand how to action the reference to achieve my goal. Commented Dec 23, 2024 at 17:45
  • That rather depends on what your goal is. Why do you want a custom allocator in the first place, and a specialized version of it for a particular class? What would that specialized version do differently? What's the ultimate point of the exercise? Commented Dec 23, 2024 at 23:36
  • 1
    A custom allocator can provide destroy(T* p) method (if not provided, std::destroy_at(p) is called, which in turn calls p->~T()). This one should be called on the "right" allocator type. This is another way, besides custom deleter, to customize destruction. Commented Dec 24, 2024 at 1:28

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.