Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: исключение при вставке в контейнер (когда недостаточно памяти)  (Прочитано 12478 раз)
0 Пользователей и 3 Гостей смотрят эту тему.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« : 17-11-2017 08:54 » 

пробую поймать исключение для ситуации, когда пытаются впихнуть невпихуемое

для вектора получается
Код: (C++)
std::vector<TYPE> cont;
while(1)
{
        try
        {
                cont.push_back(TYPE());
        }
        catch(...)
        {
                int iii=1;//попадаем
        }
}

для мапы - не получается. В дебаггере остановка происходит при очередном вызове new где-то в глубинах реализации
Код: (C++)
std::map<uint32_t,TYPE> cont;
while(1)
{
        try
        {
                cont[cont.size()]=TYPE();
        }
        catch(...)
        {
                int iii=1;//не попадаем!
        }
}

у кутешных QVector и QMap(QHash) - поведение аналогичное

как же у мапа поймать момент, когда больше нельзя добавить элементы ?  Как вариант - можно перейти к сортированному вектору, конечно. но всё же хотелось бы понять, как поймать исключение
Записан

RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 17-11-2017 20:02 » 

Леш, я не понял, какое исключение ты ловишь? std::bad_alloc?
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #2 : 18-11-2017 05:11 » 

Опероатор new опционально бросает исключение. Зависит от конкретной реализации std. Вот например, есть с noexcept и есть с throw.
Код: (C++)
//C++98
throwing (1) void* operator new (std::size_t size) throw (std::bad_alloc);
nothrow (2) void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw();
placement (3) void* operator new (std::size_t size, void* ptr) throw();

//C++11
throwing (1) void* operator new (std::size_t size);
nothrow (2) void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) noexcept;
placement (3) void* operator new (std::size_t size, void* ptr) noexcept;
Без ловли исключения, какое сообщение тестовый код бросает в консоль?
« Последнее редактирование: 18-11-2017 05:12 от Finch » Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #3 : 18-11-2017 06:00 » 

Леш, я не понял, какое исключение ты ловишь? std::bad_alloc?
ещё пока не знаю. Любое, мне надо поймать невозможность вставки, чтобы приложение не упало. Я думал, есть какое-то общее решение Улыбаюсь

Finch, я пробую в QtCreator, отладчик gdb

останавливается тут
//new_allocator.h
Код: (C++)
      // NB: __n is permitted to be 0.  The C++ standard says nothing
      // about what the return value is when __n == 0.
      pointer
      allocate(size_type __n, const void* = 0)
      {
        if (__n > this->max_size())
          std::__throw_bad_alloc();

тут==>       return static_cast<_Tp*>(::operator new(__n * sizeof(_Tp)));
      }

в консоли видно только
Invalid parameter passed to C runtime function.

в общем, ничего определённого

Записан

Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #4 : 18-11-2017 06:10 » 

Я думаю, что останавливается все таки тут
Код: (C++)
 std::__throw_bad_alloc();
там и подсказка Улыбаюсь Хочеш ловить, сам проверяй на max_size
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #5 : 18-11-2017 06:45 » new

Finch, нет, останавливается именно там, где я показал. С max_size тут не то. В реализации

Код: (C++)
      max_size() const _GLIBCXX_USE_NOEXCEPT
      { return size_t(-1) / sizeof(_Tp); }

то есть, нет проверки на недостаточность памяти (ведь память занимает не только этот контейнер)

и тест подтверждает, что это не работает

Код: (C++)
std::map<uint64_t,TYPE> cont;
while(1)
{
        try
        {
                if(cont.size()==cont.max_size())
                {
                        break;//сюда НЕ попадаем
                }
                else
                {
                        cont[cont.size()]=TYPE();
                }
        }
        catch(...)
        {
                int iii=1;//не попадаем!
        }
}



Добавлено через 43 секунды:
короче, я так понимаю, надо переходить к вектору, с мапом каши не сваришь Улыбаюсь
« Последнее редактирование: 18-11-2017 06:49 от Алексей++ » Записан

RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #6 : 18-11-2017 12:14 » 

Давай с другой стороны пойдем: а с чего приложение должно упасть? Это embedded или mobile с жестким лимитом памяти? Какая платформа?
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #7 : 18-11-2017 12:20 » 

Цитата
Invalid parameter passed to C runtime function.

Гуглеж говорит:
1) винда (кто ж еще так "информативен" в ошибках)
2) ошибка не в алокаторе, а в логике выше (лучше поищи у себя)

Цитата
::operator new
Это базовая функция выделения памяти, без привязки к типу. Фактически это malloc.
http://www.cplusplus.com/reference/new/operator%20new/
Цитата
Код:
throwing (1)	void* operator new (std::size_t size) throw (std::bad_alloc);
nothrow (2) void* operator new (std::size_t size, const std::nothrow_t& nothrow_value) throw();
placement (3) void* operator new (std::size_t size, void* ptr) throw();

У меня size_t(-1) == 18446744073709551615. Соотв., дели на sizeof своего типа. Т.е дохрена, у тебя столько памяти нет. Метод max_size стоит рассматривать как теоретический предел для данного типа. Т.е. можно вообще не рассматривать.
« Последнее редактирование: 18-11-2017 12:46 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #8 : 19-11-2017 09:21 » 

Давай с другой стороны пойдем: а с чего приложение должно упасть? Это embedded или mobile с жестким лимитом памяти? Какая платформа?
потому что оно падает, если элементов засовывать много )
У меня size_t==0xffffffff , элемент 88 байтов. Это у меня лог сообщений

Приложение - под винду, десктоп, на Qt

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

Но тут мне подумалось, что даже если я стану контролировать предел для вектора, то где-то в программе всё равно в этот момент не хватит памяти и приложение упадёт.
Видимо, придётся остановится на некой вручную подобранной цифре - максимальном количестве элементов для конкретной машины. Потому что иначе я не знаю, как это количество подобрать
Записан

Finch
Спокойный
Администратор

il
Offline Offline
Пол: Мужской
Пролетал мимо


« Ответ #9 : 19-11-2017 10:12 » 

Это сколько нужно сообшений в логе, чтобы забить около 2 гигабайт памяти (для 32 разрядной системе)? И почему нельзя скидывать на диск весь лог, при этом очишая буффер лога?
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #10 : 19-11-2017 12:21 » 

Леш, изначально неверно выбрана стратегия.
Во-первых, для лога vector не подходит из-за переалокаций, используй deque: это гибрид вектора и связанного списка.
Во-вторых, ограничение лога — необходимость даже для диска (ротация логов с архивацией ротированного файла и удалением хвоста), а уж про память и говорить не приходится. Нужен кольцевой лог. Эффективнее будет использовать пул фиксированных объектов, а в кольцевом буфере будут указатели.
Но сперва обоснуй необходимость лога в памяти, а не лога на диске. Это первый шаг к решению проблемы.
Для лога на диске в любой системе есть логер, даже в винде.

И еще: map для лога еще хуже, большой перерасход памяти. Надо понимать свойства и назначение каждого типа, их плюсы и минусы в применении к конкретно задачи.

Пример на поиграться для исследования выделения памяти под структуры STL
Код: (C++) explain_allocators.cpp
#include <cstddef>
#include <memory>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <unordered_set>
#include <map>
#include <unordered_map>
#include <functional>
#include <cstdio>

template <class T>
struct custom_allocator {
    typedef T value_type;

    custom_allocator() noexcept {
        printf("custom_allocator(): this=%p T=%lu\n", this, sizeof(T));
    }

    template <class U> custom_allocator (const custom_allocator<U>& a) noexcept {
        printf("custom_allocator(<U>%p): this=%p T=%lu U=%lu\n", &a, this, sizeof(T), sizeof(U));
    }

    T* allocate (std::size_t n) {
        void* p = malloc(n * sizeof(T));
        printf("allocate(%lu): this=%p T=%lu ptr=%p\n", n, this, sizeof(T), p);
        return static_cast<T*>(p);
    }

    void deallocate (T* p, std::size_t n) {
        printf("deallocate(%p, %lu): this=%p\n", p, n, this);
        free((void*)p);
    }
};

template <class T, class U>
constexpr bool operator== (const custom_allocator<T>&, const custom_allocator<U>&) noexcept {
    return ((sizeof(T) + 15) & ~15) == ((sizeof(U) + 15) & ~15);
}

template <class T, class U>
constexpr bool operator!= (const custom_allocator<T>& t, const custom_allocator<U>& u) noexcept {
    return !(t == u);
}

struct s1 {
    int x;
    int y;
    s1(int x = 0, int y = 0) : x(x), y(y) {
        printf("s1(int=0, int=0): %d, %d\n", x, y);
    }

    s1(const s1& s) : x(s.x), y(s.y) {
        printf("s1(const s1&): %d, %d\n", s.x, s.y);
    }

    bool operator== (const s1& s) const { return x == s.x && y == s.y; }

    bool operator<  (const s1& s) const { return x < s.x && y < s.y; }

    struct hash {
        bool operator() (const s1& s) const { return std::hash<int>()(s.x ^ s.y); }
    };
};

int main () {
    std::vector<s1> v1 = {{1, 2}, {3, 4}, {5, 6}};

    printf("\n -- vector --\n");
    std::vector<s1, custom_allocator<s1>> v(4);
    v.insert(v.end(), v1.begin(), v1.end());
    v.insert(v.end(), v1.begin(), v1.end());

    printf("\n -- deque --\n");
    std::deque<s1, custom_allocator<s1>> d(4);
    d.insert(d.end(), v1.begin(), v1.end());
    d.insert(d.end(), v1.begin(), v1.end());

    printf("\n -- list --\n");
    std::list<s1, custom_allocator<s1>> l(4);
    l.insert(l.end(), v1.begin(), v1.end());
    l.insert(l.end(), v1.begin(), v1.end());

    printf("\n -- set --\n");
    std::set<s1, std::less<s1>, custom_allocator<s1>> s;
    s.insert(v1.begin(), v1.end());
    s.insert(v1.begin(), v1.end());

    printf("\n -- multiset --\n");
    std::multiset<s1, std::less<s1>, custom_allocator<s1>> ms;
    ms.insert(v1.begin(), v1.end());
    ms.insert(v1.begin(), v1.end());

    printf("\n -- unordered_set --\n");
    std::unordered_set<s1, s1::hash, std::equal_to<s1>, custom_allocator<s1>> us;
    us.insert(v1.begin(), v1.end());
    us.insert(v1.begin(), v1.end());

    printf("\n -- unordered_multiset --\n");
    std::unordered_multiset<s1, s1::hash, std::equal_to<s1>, custom_allocator<s1>> ums;
    ums.insert(v1.begin(), v1.end());

    printf("\n -- finish --\n");
    return 0;
}

gcc -Wextra -O2 -o explain_allocators explain_allocators.cpp -lstdc++ -std=c++11
« Последнее редактирование: 19-11-2017 12:36 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Алексей++
глобальный и пушистый
Глобальный модератор

ru
Offline Offline
Сообщений: 13


« Ответ #11 : 20-11-2017 04:45 » 

Finch, ну, его там надо пооптимизировать чутка по размеру записи, но вопрос был не об этом )

Ром, я понимаю плюсы и минусы контейнеров. Сейчас там мап - такое наследие, буду делать вектор. Дек не хочу, переаллокации в векторе мне не страшны, они там ни на чём не скажутся. Логи пока что нужны только в озу, если что, добавим БД

В основном я тут хотел узнать только ситуацию с мапом - ловится ли такое исключение. Как я понимаю - не ловится.

А до исключения доводить не буду, видимо, при помощи настроек и размера лога Улыбаюсь
Записан

Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines