Наши проекты:
Журнал · Discuz!ML · Wiki · DRKB · Помощь проекту |
||
ПРАВИЛА | FAQ | Помощь | Поиск | Участники | Календарь | Избранное | RSS |
[3.145.12.242] |
|
Страницы: (2) [1] 2 все ( Перейти к последнему сообщению ) |
Сообщ.
#1
,
|
|
|
Есть класс, у него есть функция "отправить что угодно заданного размера". Есть шаблонная функция, которой можно передать что угодно, функция сама подставит размер:
class a { public: void send(void const * from, size_t size); template <typename T> void send(T const & data) { send(&data, sizeof(T); } } По условиям протокола передачи строки передаются без завершающего нуля. Описанная выше реализация при вызове send("слава мне, победителю драконов!") отправляет строку с завершающим нулем. приходится писать что-то вроде { char const String[] = "слава мне, победителю драконов!"; A.send(String, sizeof(String) - 1); } Конечно, это можно обернуть в макрос, но душа желает красоты. Я хочу переопределить функцию send(), чтобы она сама вычитала единицу из размера, если ей передали константный массив char. Ни один из следующих вариантов не срабатывает, вместо них раскручивается указанная выше реализация шаблона: template<size_t length> void response(char const string[length]) { response(string, length - 1); } template<size_t length, char const T[length]> void response(char const string[length]) { response(string, length - 1); } Я хочу чего-то невозможного? |
Сообщ.
#2
,
|
|
|
void send(const char *data) { send(data, strlen(data)); } template <size_t length> void send(const char (&data)[length]) { send(&data, length-1); } |
Сообщ.
#3
,
|
|
|
Спасибо. Да уж, сильно меня переклинило. Только send(data, length-1); должно быть, без "&".
|
Сообщ.
#4
,
|
|
|
Цитата Dushevny @ Ну я бы не назвал это очевидным решением. Массивы, строковые литералы не исключение, сводятся к указателям, поэтому указание размера массива в параметре функции по сути является комментарием и потому игнорируется. Но сведение не осуществляется для ссылочных типов, поэтому компилятор сможет вывести размер массива, если он известен в точке вызова.Да уж, сильно меня переклинило Цитата Dushevny @ Почему? Основная же функция send(void const*, size_t) или я что-то не понял? Только send(data, length-1); должно быть, без "&". |
Сообщ.
#5
,
|
|
|
Цитата Qraizer @ Да знал я про такой синтаксис при объявлении специализации для массива, но... забыл.Ну я бы не назвал это очевидным решением. Цитата Qraizer @ Потому что массив неявно приводится к типу "указатель на нулевой элемент". &data тоже компилится, но лишняя писанина. Почему? Основная же функция send(void const*, size_t) или я что-то не понял? |
Сообщ.
#6
,
|
|
|
Цитата Dushevny @ Я хочу переопределить функцию send(), чтобы она сама вычитала единицу из размера, если ей передали константный массив char. Попробуй так: class a { public: // void send(const void* from, size_t size); void send(const char* from, size_t size=0); template <typename T> void send(T const & data) { send((const char*)&data, sizeof(T)); } }; void a::send(const char* from, size_t size) { if(size==0) size=strlen(from); //... } Тогда: char const* pString = "слава мне, победителю драконов!"; A.send(pString); A.send("ура, ура, ура !"); |
Сообщ.
#7
,
|
|
|
Цитата ЫукпШ @ Не годится. Если я захочу передать структуру - ваш код применит к ней strlen(). К тому же проверка лишнего условия на этапе выполнения. Можно переопределить функцию для char const * без шаблона, но остается лишний вызов strlen() для строки, размер которой известен компилятору. Конечно, если эту функцию сделать встраиваемой, то современные оптимизаторы чаще всего заменят вызов strlen() на известный им размер, но Qraizer уже подсказал правильное решение. Именно то, которое было нужно. Попробуй так: |
Сообщ.
#8
,
|
|
|
Цитата Dushevny @ Цитата ЫукпШ @ Не годится. Если я захочу передать структуру - ваш код применит к ней strlen().Попробуй так: Нет. strlen будет применён только для типа const char*. Для других типов будет использована шаблонная функция. А для них sizeof(T) не равен 0. |
Сообщ.
#9
,
|
|
|
Цитата ЫукпШ @ Ага, понял. Невнимательно прочитал ваш код. Все равно некрасиво: основная функция для передачи чего угодно принимает указатель на char const *, хотя в языке для таких случаев существует специальный тип "указатель на что угодно", т.е. void (const) *. И в шаблоне приходится явно приводить указатель на переданный тип к char const *, и в почти любой другой точке вызова основной функции придется засорять код явным приведением к char const *, в то время как к void const * приведение осуществляется неявно. В общем, работать оно будет, но рушится строгая типизация. Неаккуратненько, а потому некрасиво. strlen будет применён только для типа const char*. |
Сообщ.
#10
,
|
|
|
Цитата Dushevny @ В общем, работать оно будет, но рушится строгая типизация. Неаккуратненько, а потому некрасиво. Воспользуйся string_view. Здесь при помощи первой функции можно отправлять указатели, второй - любые строки, третьей - все остальные типы (желательно добавить в шаблон ещё проверку, что это pod) void send(const void *data, size_t sz); void send(const std::string_view &sv) {send(sv.data(), sv.size());} template <typename T, typename = std::enable_if_t<!std::is_convertible_v<std::string_view>>> void send(const T &val) {send(&val, sizeof(val));} |
Сообщ.
#11
,
|
|
|
Цитата Dushevny @ Надеюсь, ты понимаешь, что этот код легко ломается обычным массивом, типа char msg[256] = { "Hello, world!" }; Qraizer уже подсказал правильное решение. Именно то, которое было нужно. |
Сообщ.
#12
,
|
|
|
Цитата Qraizer @ Цитата Dushevny @ Надеюсь, ты понимаешь, что этот код легко ломается обычным массивом, типа char msg[256] = { "Hello, world!" };Qraizer уже подсказал правильное решение. Именно то, которое было нужно. А почему ? |
Сообщ.
#13
,
|
|
|
Потому что будет передан весь массив на 256 символов вместо только строки без NUL в конце.
|
Сообщ.
#14
,
|
|
|
Цитата Qraizer @ Потому что будет передан весь массив на 256 символов вместо только строки без NUL в конце. Qraizer, "char msg[256]", передаваемый как "msg" будет воспринят компилятором как "char* msg". Из предложенных прототипов функций немедленно будет выбрана функция "send(const char*)", поскольку преобразование типа char* -> const char* полностю допустимо и компилятор сделает его обязательно. --- Вот эта конструкция: char msg[256]="123"; // или char msg[256]={"345"}; приведёт к тому, что будет выделен массив указанных размеров, в который сначала будет помещена указанная строка, а весь остаток будет занулён. Таким образом, вышеупомянутый вариант это, как бы, строка с длинным нулевым "хвостом". А попытка сделать : char msg[3]="123"; приведёт к синтаксической ошибке. |
Сообщ.
#15
,
|
|
|
Цитата ЫукпШ @ char msg[256], передаваемый как "msg" для компилятора будет char* msg. Из предложенных прототипов функций немедленно будет выбрана функция "send(const char* ...", поскольку преобразование типа char* -> const char* полностю допустимо и компилятор сделает его обязательно. С чего вдруг? У него вызовется вот эта функция, при передаче массива. Цитата Qraizer @ template <size_t length> void send(const char (&data)[length]) { send(&data, length-1); } Так как она как раз массив принимает, а char msg[256] - это в первую очередь массив. |