В проекте много использований стандартных 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];
}
}
ну раз никто не отвечает, можно я вставлю свои 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 данных
Z>то прога должна валиться в кору!
Здесь вопроса нет, вопрос в том, по какой причине. Если она, например, валится в кору по SIGBUS, с порчей стека, то фиг потом ты по этой корке поймешь, где она у тебя слетела.
Так что валиться она должна в удобочитаемом виде непосредственно в момент обращения. И это можно клево сделать исключением.
А если читать мои сообщения в соседних постингах, и разговоре о том, что catch в программе должен быть ровно один — то все стает на свои места.
Да здравствует мыло душистое и веревка пушистая.
Re[3]: контейнер-обёртка для const данных и не const данных
Здравствуйте, 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 данных
Здравствуйте, Alexander Pazdnikov, Вы писали:
AP>просто обёртка проверки контроля доступа к элементам и кидания исключения при выходе за границы массива.
одномерный boost::multi_array_ref
Здравствуйте, 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 данных
zaufi:
Z>в целом подитоживая: исключения нужны когда мы хотим реагировать на ошибку
Если точнее, исключительную ситуацию (необязательно ошибку).
Z>кроме того, нет ни какой разумной реакции на выход за границы массива!
Если речь о release-версии, то можно попытаться сохранить несохранённые данные и потом завершиться, вместо того чтобы сразу упасть.
Z>в низкоуровневых вещах нужно расположить параноидальные ассерты НА ВСЕ, чтобы контролировать все входящие и выходящие параметры, внутренее состояние после каждого чиха, и т.д. --- ассертов много не бывает! ассерты -- друг программиста.
В отладочной версии — да, но в release-версии логические ошибки имеет смысл обрабатывать посредством исключений (std::logic_error создан как раз для этих целей). Естественно, стоимость таких исключений нужно принимать во внимание.
Z>но в целом мысль вот в чем: вместо того чтобы бороться с последствиями, нужно просто их не допускать! -- т.е. по простому: нужно просто писать проги, которые не глючат )))
Легко сказать. Чем сложнее программа, тем больше вероятность того, что она будет глючить, даже несмотря на тщательное тестирование.