На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Qraizer, Hsilgos
Страницы: (2) [1] 2  все  ( Перейти к последнему сообщению )  
> специализация шаблона функции для константной строки - литерала
    Есть класс, у него есть функция "отправить что угодно заданного размера". Есть шаблонная функция, которой можно передать что угодно, функция сама подставит размер:
    ExpandedWrap disabled
      class a
      {
      public:
         void send(void const * from, size_t size);
       
         template <typename T>
         void send(T const & data)   { send(&data, sizeof(T); }
      }


    По условиям протокола передачи строки передаются без завершающего нуля. Описанная выше реализация при вызове send("слава мне, победителю драконов!") отправляет строку с завершающим нулем. приходится писать что-то вроде
    ExpandedWrap disabled
      {
        char const String[] = "слава мне, победителю драконов!";
        A.send(String, sizeof(String) - 1);
      }

    Конечно, это можно обернуть в макрос, но душа желает красоты.

    Я хочу переопределить функцию send(), чтобы она сама вычитала единицу из размера, если ей передали константный массив char.
    Ни один из следующих вариантов не срабатывает, вместо них раскручивается указанная выше реализация шаблона:
    ExpandedWrap disabled
          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); }


    Я хочу чего-то невозможного?
      ExpandedWrap disabled
        void send(const char *data) { send(data, strlen(data)); }
         
        template <size_t length>
        void send(const char (&data)[length]) { send(&data, length-1); }
      Сообщение отредактировано: Qraizer -
        Спасибо. Да уж, сильно меня переклинило. Только send(data, length-1); должно быть, без "&".
          Цитата Dushevny @
          Да уж, сильно меня переклинило
          Ну я бы не назвал это очевидным решением. Массивы, строковые литералы не исключение, сводятся к указателям, поэтому указание размера массива в параметре функции по сути является комментарием и потому игнорируется. Но сведение не осуществляется для ссылочных типов, поэтому компилятор сможет вывести размер массива, если он известен в точке вызова.
          Цитата Dushevny @
          Только send(data, length-1); должно быть, без "&".
          Почему? Основная же функция send(void const*, size_t) или я что-то не понял?
            Цитата Qraizer @
            Ну я бы не назвал это очевидным решением.
            Да знал я про такой синтаксис при объявлении специализации для массива, но... забыл.
            Цитата Qraizer @
            Почему? Основная же функция send(void const*, size_t) или я что-то не понял?
            Потому что массив неявно приводится к типу "указатель на нулевой элемент". &data тоже компилится, но лишняя писанина.
            Сообщение отредактировано: Dushevny -
              Цитата Dushevny @
              Я хочу переопределить функцию send(), чтобы она сама вычитала единицу из размера, если ей передали константный массив char.

              Попробуй так:
              ExpandedWrap disabled
                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);
                //...
                }


              Тогда:
              ExpandedWrap disabled
                  char const* pString = "слава мне, победителю драконов!";
                  A.send(pString);
                  A.send("ура, ура, ура !");
              Сообщение отредактировано: ЫукпШ -
                Цитата ЫукпШ @
                Попробуй так:
                Не годится. Если я захочу передать структуру - ваш код применит к ней strlen(). К тому же проверка лишнего условия на этапе выполнения. Можно переопределить функцию для char const * без шаблона, но остается лишний вызов strlen() для строки, размер которой известен компилятору. Конечно, если эту функцию сделать встраиваемой, то современные оптимизаторы чаще всего заменят вызов strlen() на известный им размер, но Qraizer уже подсказал правильное решение. Именно то, которое было нужно.
                  Цитата Dushevny @
                  Цитата ЫукпШ @
                  Попробуй так:
                  Не годится. Если я захочу передать структуру - ваш код применит к ней strlen().

                  Нет.
                  strlen будет применён только для типа const char*.
                  Для других типов будет использована шаблонная функция.
                  А для них sizeof(T) не равен 0.
                  Сообщение отредактировано: ЫукпШ -
                    Цитата ЫукпШ @
                    strlen будет применён только для типа const char*.
                    Ага, понял. Невнимательно прочитал ваш код. Все равно некрасиво: основная функция для передачи чего угодно принимает указатель на char const *, хотя в языке для таких случаев существует специальный тип "указатель на что угодно", т.е. void (const) *. И в шаблоне приходится явно приводить указатель на переданный тип к char const *, и в почти любой другой точке вызова основной функции придется засорять код явным приведением к char const *, в то время как к void const * приведение осуществляется неявно. В общем, работать оно будет, но рушится строгая типизация. Неаккуратненько, а потому некрасиво.
                    Сообщение отредактировано: Dushevny -
                      Цитата Dushevny @
                      В общем, работать оно будет, но рушится строгая типизация. Неаккуратненько, а потому некрасиво.

                      Воспользуйся string_view. Здесь при помощи первой функции можно отправлять указатели, второй - любые строки, третьей - все остальные типы (желательно добавить в шаблон ещё проверку, что это pod)


                      ExpandedWrap disabled
                        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));}
                        Цитата Dushevny @
                        Qraizer уже подсказал правильное решение. Именно то, которое было нужно.
                        Надеюсь, ты понимаешь, что этот код легко ломается обычным массивом, типа char msg[256] = { "Hello, world!" };
                          Цитата Qraizer @
                          Цитата Dushevny @
                          Qraizer уже подсказал правильное решение. Именно то, которое было нужно.
                          Надеюсь, ты понимаешь, что этот код легко ломается обычным массивом, типа char msg[256] = { "Hello, world!" };

                          А почему ?
                            Потому что будет передан весь массив на 256 символов вместо только строки без NUL в конце.
                              Цитата Qraizer @
                              Потому что будет передан весь массив на 256 символов вместо только строки без NUL в конце.

                              Qraizer,
                              "char msg[256]", передаваемый как "msg" будет воспринят компилятором как "char* msg".
                              Из предложенных прототипов функций немедленно будет выбрана функция
                              "send(const char*)", поскольку преобразование типа char* -> const char*
                              полностю допустимо и компилятор сделает его обязательно.
                              ---
                              Вот эта конструкция:
                              ExpandedWrap disabled
                                char msg[256]="123";  
                                // или
                                char msg[256]={"345"};

                              приведёт к тому, что будет выделен массив указанных размеров,
                              в который сначала будет помещена указанная строка, а весь остаток будет занулён.
                              Таким образом, вышеупомянутый вариант это, как бы, строка с длинным нулевым "хвостом".
                              А попытка сделать :
                              ExpandedWrap disabled
                                char msg[3]="123";

                              приведёт к синтаксической ошибке.
                              Сообщение отредактировано: ЫукпШ -
                                Цитата ЫукпШ @
                                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] - это в первую очередь массив.
                                0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                                0 пользователей:


                                Рейтинг@Mail.ru
                                [ Script execution time: 0,0649 ]   [ 17 queries used ]   [ Generated: 19.03.24, 03:38 GMT ]