Как определить, есть ли у объекта выделенные элементы в куче? - PullRequest
4 голосов
/ 30 июня 2019

Есть ли способ определить, есть ли у объекта хотя бы один из его элементов в куче?

Я пытаюсь различать такие объекты, как std::string или std::vector или std::list(да, в основном это контейнеры) из всех других типов (к сожалению, даже контейнеры с одним элементом находятся в моей "сфере интересов")

Я пытаюсь сделать что-то вроде следующего:

struct foo
{
private:
    int * _ptr;

public:
    foo() : _ptr(new int) {};
    ~foo() { delete _ptr; };
};

struct bar
{
private:
    int val;
};

template <typename T>
void func(T val)
{
    if constexpr (std::is_class_v<T>)
    {
        std::cout << std::setw(20) << typeid(T).name() << " is a class type." << std::endl;
        if (/* determine if foo has any heap allocations */)
        {
            // Do something #1.
            std::cout << std::setw(20) << typeid(T).name() << " does allocate on heap." << std::endl;
        }
        else
        {
            // Do something #2.
            std::cout << std::setw(20) << typeid(T).name() << " does NOT allocate on heap." << std::endl;
        }
    }
    else
    {
        // Do something #3.
        std::cout << std::setw(20) << typeid(T).name() << " is NOT a class type." << std::endl;
    }
}

int main()
{
    func(foo()); // foo does allocate on heap
    cout << endl;

    func(bar()); // bar does NOT allocate on heap
    cout << endl;
};

классы foo и bar являются лишь примерами, а функция func() должна выполнять функции, немного отличающиеся от cout в консоли.

Ответы [ 5 ]

4 голосов
/ 30 июня 2019

Это проблема XY ... Я пытаюсь написать хеш-функцию, которая будет знать, хранит ли пользовательский тип ключа какие-либо данные в массиве (так же, как std :: string`). Я вполне могу создать общий хэш-функция, которая принимает любой тип, но если этот тип имеет массив ( указатель) на некоторые сохраненные данные, проблемы становятся очень сложными (указатели на динамические массивы меняются во время выполнения, тот же ключ в разное время приведет к разному значению хеша)

Имейте в виду, что некоторые типы структур / классов имеют внутреннее неинициализированное заполнение, которое может привести к некорректной работе вашей хэш-функции.

В противном случае, std::is_trivially_copyable было бы хорошим началом.

3 голосов
/ 30 июня 2019

Хотя я почти уверен, что другие ответы верны, когда они говорят, что вам не следует связываться с попыткой определить, находится ли объект в куче или стеке, я должен не согласиться, что это «невозможно» определить.

В следующем примере используются окна (и процедура будет отличаться для каждой ОС!):

#include <Windows.h>
bool isAddressOnHeap(void* address) {
    bool returnval = 0;
    PROCESS_HEAP_ENTRY entry;
    HANDLE procHeap = GetProcessHeap();
    if (procHeap == NULL) {
        std::cerr << "Failed to retrieve Heap.\n";
        return -1;
    }

    if (HeapLock(procHeap) == FALSE) {
        std::cerr << "Failed to lock heap.\n";
        return -1;
    }

    entry.lpData = NULL;
    if (HeapWalk(procHeap, &entry) != 0) {
        if (entry.wFlags & PROCESS_HEAP_REGION) {
            std::cout << "Function succeeded. The heap begins at " << (void*)entry.Region.lpFirstBlock << " and ends at " << (void*)entry.Region.lpLastBlock << "\n";
            if (address >= (void*)entry.Region.lpFirstBlock && address <= (void*)entry.Region.lpLastBlock) 
                returnval = 1;
        }
    }

    if (HeapUnlock(procHeap) == FALSE) {
        std::cerr << "Failed to unlock heap.\n";
        return -1;
    }
    return returnval;
}

Эта функция определит, находится ли данный адрес в куче или нет.Вот тестовая программа:

int main()
{
    int i = 1;
    std::cout << "Address of i on Stack: " << (void*)&i << "\n";


    int* j = new int;
    *j = 1;
    std::cout << "Address of j on Heap: " << (void*)j << "\n";

    int* k = (int*)malloc(sizeof(int));
    *k = 1;
    std::cout << "Address of k on Heap: " << (void*)k << "\n";


    std::cout << "is i on Heap?: " << isAddressOnHeap(&i) << "\n";
    std::cout << "is j on Heap?: " << isAddressOnHeap(j) << "\n";
    std::cout << "is k on Heap?: " << isAddressOnHeap(k) << "\n";

    delete j;
    free(k);
    return 0;
}

Программа возвращает на моем компьютере следующее:

Address of i on Stack: 0000001062AFFE80
Address of j on Heap: 0000018B11C530B0
Address of k on Heap: 0000018B11C53070
Function succeeded. The heap begins at 0000018B11C40740 and ends at 0000018B11D3F000
is i on Heap?: 0
Function succeeded. The heap begins at 0000018B11C40740 and ends at 0000018B11D3F000
is j on Heap?: 1
Function succeeded. The heap begins at 0000018B11C40740 and ends at 0000018B11D3F000
is k on Heap?: 1

Подробнее о том, как работает этот код, можно узнать на сайте Microsoft .

На компьютерах с Linux, похоже, аналогичные способы сделать это делают.

Но в качестве последнего слова предупреждения: этот ответ является лишь подтверждением концепции!

3 голосов
/ 30 июня 2019

Как отмечали другие в комментариях, нет способа различить, указывает ли указатель на динамически выделенный или нет.С другой стороны, стандарт C ++ <type_traits> предоставляет черты типа, такие как

и так далее.Посмотрите на ссылки, соответствует ли это вашей реальной проблеме.


При этом, почему это не может быть решено с помощью функции-члена, которая сообщает информацию, что вам нужно?

Например:

struct foo
{
public:
    bool isHeapAllocated() const noexcept { return true; }
};

struct bar
{
public:
    bool isHeapAllocated() const noexcept { return false; }
};

Затем просто вызовите функцию:

template <typename T> void func(T val)
{
    if constexpr (std::is_class_v<T>)
    {
        if (val.isHeapAllocated()) { 
            // Do something #1.
        }
        else
        {
            // Do something #2.
        }
    }
    else
    {
        // Do something #3.
    }
}
2 голосов
/ 30 июня 2019

Я пытаюсь провести различие между «типом класса с массивом в качестве члена» и «типом класса без массива в качестве члена».

Для этого вам нужно взглянуть на код, поэтому вам нужно передать вашей функции путь к исходному коду и вам нужно его проанализировать.

1 голос
/ 02 июля 2019

Единственный верный способ - добавить необходимое оборудование для обработки каждого типа и, при необходимости, отдельный объект.

Следующее является хорошим началом для любых здравомыслящих типов (с соблюдением правила ноль / три / пять):

template <class T>
constexpr auto owns_no_dynamic_memory(T const& x) noexcept {
    if constexpr (std::is_trivially_copyable_v<T>
    || std::is_trivially_move_constructible_v<T>
    || std::is_trivially_copy_assignable_v<T>
    || std::is_trivially_move_assignable_v<T>
    || std::is_trivially_destructible_v<T>)
        return std::true_type();
    else
        return false;
}

При необходимости добавьте перегрузки.

...