контейнер-обёртка для const данных и не const данных
От: Alexander Pazdnikov  
Дата: 29.07.11 07:21
Оценка:
Здравствуйте, Коллеги.

Прошу у вас помощи.

В проекте много использований стандартных C-массивов и функций куда эти массивы передаются

struct A
{
    size_t m_arr_cnt;

    do_write(int *arr)
    {
        for (int i = 0; i < m_arr_cnt; ++i) /* проверка на границу по внешней переменной */
        {
            arr[i] = rand(); 
        }
    }

    do_read(int *arr)
    {
        for (int i = 0; i < m_arr_cnt; ++i) /* проверка на границу по внешней переменной */
        {
            printf("%i\n") = arr[i];
        }
    }

};

Хочу написать безопасную обёртку ala boost::array, но без копирования элементов, 
просто обёртка проверки контроля доступа к элементам и кидания исключения при выходе за границы массива.

Использовать вот так 

[ccode]
    do_write(carray<int> arr)
    {
        for (int i = 0; i < arr.size(); ++i) /* проверка на границу по внешней переменной */
        {
            arr[i] = rand(); 
        }
    }


    do_read(const carray<int> &arr)
    {
        for (int i = 0; i < arr.size(); ++i) /* проверка на границу по внешней переменной */
        {
            printf("%i\n") = arr[i];
        }
    }


Для констант написал

#include <cstdlib>

#include <algorithm>
#include <iostream>
#include <iterator>

#include <stdexcept>

using namespace std;

/**
 * Обёртка безопасного использования С-массивов.
 * Можно спокойно передавать параметром по значению на стеке
 */
template <typename T>
struct carray
{
    template <size_t SIZE> carray(const T (&arr)[SIZE] ) :
        m_arr(&arr[0]),
        m_size(SIZE)
    {}

    const T* begin() const { return &m_arr[0]; }
    const T* end() const { return &m_arr[m_size]; }

    const T& operator[](size_t idx)
    {
        if (idx >= m_size)
        {
            throw range_error("");
        }
        return m_arr[idx];
    }

    size_t size() const { return m_size; }


private:
    const T * const m_arr;
    const size_t m_size;
};

/*
 *
 */
int main(int argc, char** argv)
{
    const size_t arr[] = {1, 2, 3};

    carray<size_t> a(arr);

    copy(a.begin(), a.end(), ostream_iterator<size_t>(cout, ","));


    for (size_t i = 0; i < a.size(); ++i)
    {
        cout << a[i] << endl;
    }

    return 0;
}


, но как теперь расширить caaray на неконстантные массивы, которые изменяются в do_write ?

Или писать один для констант carray_const и второй для неконстант carray ?
Re: контейнер-обёртка для const данных и не const данных
От: zaufi Земля  
Дата: 29.07.11 15:25
Оценка: 3 (1) -1
ну раз никто не отвечает, можно я вставлю свои 5 копеек )
--
я бы вообще не заморачивался ни с какими обертками ) -- есть же прекрасная библа boost::range !

создавай себе из своих массивов boost::iterator_range (см. make_iterator_range или as_array) и пользуйся на здоровье как захочется -- хоть в алгоритмы, суй хоть адаптеры пользуй...

что еще надо? хочешь получать исключения при попытке выйти за границы массива? -- ну тут могут быть такие решения:
* наследоваться от boost::iterator_range (понимая что ты Random Access Range) и переопределить его operator[] -- кидай там все что захочешь

ну а второй вариант не захламлять свою программу оверхедом на пустом месте -- мое такое imho, нафиг не нужны тут никакие исключения!
если программист написал кривую прогу и не может обеспечить в своем API контроль правильности вызываемых параметров, то прога должна валиться в кору! -- ибо юзеру не интересно сообщение вида "выход за границы массива", ему интересна работающая программа! обязанность программиста просто не допускать такого выхода за границы (валидируя юзерский инпут, и сообщая ему что он дурак заранее) -- а выход за границы это вовсе не исключительная ситуация, а критическая! именно по этому прога должна сливать корку, чтобы программер высунув руки из задницы, взял отладчик и расковырял бэктрэйс (и именно по этому в коде operator[] у range стоит assert проверяющий границы).
Re[2]: контейнер-обёртка для const данных и не const данных
От: Vamp Россия  
Дата: 29.07.11 18:31
Оценка:
Z>то прога должна валиться в кору!
Здесь вопроса нет, вопрос в том, по какой причине. Если она, например, валится в кору по SIGBUS, с порчей стека, то фиг потом ты по этой корке поймешь, где она у тебя слетела.
Так что валиться она должна в удобочитаемом виде непосредственно в момент обращения. И это можно клево сделать исключением.
А если читать мои сообщения в соседних постингах, и разговоре о том, что catch в программе должен быть ровно один — то все стает на свои места.
Да здравствует мыло душистое и веревка пушистая.
Re[3]: контейнер-обёртка для const данных и не const данных
От: zaufi Земля  
Дата: 29.07.11 19:59
Оценка: 3 (1) +1
Здравствуйте, Vamp, Вы писали:

Z>>то прога должна валиться в кору!

V>Здесь вопроса нет, вопрос в том, по какой причине. Если она, например, валится в кору по SIGBUS, с порчей стека, то фиг потом ты по этой корке поймешь, где она у тебя слетела.

надеюсь нет ведь сомнений, что правильно выставленный assert проверяющий правильность индекса (как раз перед тем как сделать обращение по неправильному адресу) уронит прогу как раз в том месте, где по корке и значениям переменных можно будет понять причину, а также как мы сюда попали? -- т.е. ни каких SIGBUS и SIGSEGV мы не допустим и ни каких поломаных стэков... упадет ровно в том месте где чуть не произошла проблема! ключевое слово "чуть не произошла"...

V>Так что валиться она должна в удобочитаемом виде непосредственно в момент обращения. И это можно клево сделать исключением.


улыбает подобная наивность ))
вот есть у тебя функция строк на N (вызывающая кучу других не менее маленьких), активно работающая с херней которую хочет получить ТС -- т.е. в сумме имеем скажем строк 500-1000 кода в котором вылетает std::out_of_range... и, забегая вперед, про очередную смешную мысль о единственности catch, вот вылетела твоя прога установленная у клиента на боевой сервер, в тот единственный catch... что дальше? твои действия? (ну кроме естественно выдачи очень полезного для пользователя сообщения "извнини, у мя руки из жопы, и я не знаю в каком месте моей проги я облажался с индексом при обращении к массиву. твоим данным кабзда. я вынужден завершить программу, потому что сделать уже нихера нельзя...". будешь читать лог? -- поезд то ведь ушел уже, исключение было поймано, контекст в котором произошла проблема уже утерян! но очевидно ведь, что все сведется к тому, что нужно будет расставлять отладочные сообщения ВЕЗДЕ! даже в дурацкой функции operator[]! ну это же бред бредячий! если есть более менее сложная система, с мега логгером, тебе придется "знакомить" с логгерским интерфейсом очень, ОЧЕНЬ низкоуровневые\элементарные вещи (которые по сути сами проще устроены чем логгер)...

а с мыслью о "клево можно сделать", если гипертрофировать, можно докатиться до внедрения ГУЯ в обертку над массивами )) -- а чо, надо же както показывать сообщения пользователю ))

V>А если читать мои сообщения в соседних постингах, и разговоре о том, что catch в программе должен быть ровно один — то все стает на свои места.


так и есть! почитал... все встало на свои места )) -- у вас просто нет опыта работы с исключениями, крайне поверхностные поняти я том, как дизайнятся проги работающие с исключениеми, но советовать вы тем не менее любите )))

---
в целом подитоживая: исключения нужны когда мы хотим реагировать на ошибку. причем под реакцией подразумевается не тупо печать сообщения и выход! а даже так: конкретный тип исключения для конкретной исключительной ситуации, которую мы хотим отличать от другой!
в низкоуровневые вещи типа дурацкой обертки у ТС НЕ НУЖНО ТАЩИТЬ ИСКЛЮЧЕНИЯ -- нахрен не нужен оверхед на пустом месте! (ну вот если вы понимаете ассемблер, то должны знать что обращение к массиву по индексу произведет 1-2 машинные инструкции -- функция будет заинлайнена по самое нибалуй... внесение же сюда try/catch увеличит эту функцию в разы (!) (не буду брать с потолка цифр, а проверять лень -- но это самоочевидно). в результате те функции которые бы могли быть инлайновыми перестанут ими быть... внезапно пропадет возможность сделать там автовекторизацию или, допустим эффективно запайплайнить соседние инструкции... да мало ли. *НАХРЕН* такие функции! кроме того, нет ни какой разумной реакции на выход за границы массива! (а какая? вместо обращения к массву соединений, твой сервер просто откажется выполнять работу, но блин при этом не упадет? -- офигенная надежность! или там редактор какой вместа сохранить документ отругается, что произошел выход за границу массива? -- мне смешно... пользователю не будет!)
не нужно путать теплое с мягким! вот есть, допустим printf: вас не удивляет что НЕ НУЖНО СЮДА ПЕРЕДАВАТЬ кривые указатели в качестве форматной сроки? находясь в здравом уме никто этого не позволит! так же и тут: в низкоуровневых вещах нужно расположить параноидальные ассерты НА ВСЕ, чтобы контролировать все входящие и выходящие параметры, внутренее состояние после каждого чиха, и т.д. --- ассертов много не бывает! ассерты -- друг программиста. инстумент позволяющий валидировать, что функция выполнят свой контракт, и выполняет его правильно (+ юнит тесты конечно же!). в документации на функцию должно быть четко написано, что она ожидает (preconditions) и что будет когда мы из нее выйдем (postconditions). ... хотя практика параноидальных ассертов касается не только низкоуровневых абстракций... но в целом мысль вот в чем: вместо того чтобы бороться с последствиями, нужно просто их не допускать! -- т.е. по простому: нужно просто писать проги, которые не глючат )))

на этой радостной ноте давайте закончим эту бесплодную болтавню, и отпразднуем день сисадмина!!!
Re: контейнер-обёртка для const данных и не const данных
От: jazzer Россия Skype: enerjazzer
Дата: 01.08.11 03:12
Оценка:
Здравствуйте, Alexander Pazdnikov, Вы писали:

AP>просто обёртка проверки контроля доступа к элементам и кидания исключения при выходе за границы массива.

одномерный boost::multi_array_ref
jazzer (Skype: enerjazzer) Ночная тема для RSDN
Автор: jazzer
Дата: 26.11.09

You will always get what you always got
  If you always do  what you always did
Re[2]: контейнер-обёртка для const данных и не const данных
От: Alexander Pazdnikov  
Дата: 01.08.11 05:44
Оценка:
Здравствуйте, zaufi, Вы писали:

Z>я бы вообще не заморачивался ни с какими обертками ) -- есть же прекрасная библа boost::range !

Спасибо, что предотвратили велосипедостроительство , воспользовался boost::range
do_read(const boost::iterator_range<const int *> &arr);
...

Отлично скомпилилось и работает без изменений вызывающего кода. Как раз то что нужно было.

Z>ну а второй вариант не захламлять свою программу оверхедом на пустом месте -- мое такое imho, нафиг не нужны тут никакие исключения!

Согласен, только у меня встраиваемая система, может стоять где-нибудь в тундре, куда только на вертолёте долететь можно или на танке два дня пути , поэтому апгрейд firmware должен оставлять возможность следующего апгрейда. В случае с assert и ошибки в программе придётся ехать на объект Поэтому и исключения, пробовал boost::exception, она даёт возможность вернуть call-trace, но компилятор слишком древний (gcc 3.2.3).
Re[4]: контейнер-обёртка для const данных и не const данных
От: Masterkent  
Дата: 01.08.11 07:11
Оценка: +1
zaufi:

Z>в целом подитоживая: исключения нужны когда мы хотим реагировать на ошибку


Если точнее, исключительную ситуацию (необязательно ошибку).

Z>кроме того, нет ни какой разумной реакции на выход за границы массива!


Если речь о release-версии, то можно попытаться сохранить несохранённые данные и потом завершиться, вместо того чтобы сразу упасть.

Z>в низкоуровневых вещах нужно расположить параноидальные ассерты НА ВСЕ, чтобы контролировать все входящие и выходящие параметры, внутренее состояние после каждого чиха, и т.д. --- ассертов много не бывает! ассерты -- друг программиста.


В отладочной версии — да, но в release-версии логические ошибки имеет смысл обрабатывать посредством исключений (std::logic_error создан как раз для этих целей). Естественно, стоимость таких исключений нужно принимать во внимание.

Z>но в целом мысль вот в чем: вместо того чтобы бороться с последствиями, нужно просто их не допускать! -- т.е. по простому: нужно просто писать проги, которые не глючат )))


Легко сказать. Чем сложнее программа, тем больше вероятность того, что она будет глючить, даже несмотря на тщательное тестирование.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.