Помощь - Поиск - Пользователи - Календарь
Полная версия этой страницы: Возврат значения char */char const *
Форум разработчиков электроники ELECTRONIX.ru > Микроконтроллеры (MCs) > Cредства разработки для МК
jcxz
Имеется функция:
char *StrFunc(char const *str) {}
Внутри она использует str именно как char const * (только чтение) и потом возвращает например: str+N (N - типа int).
Но в качестве вх. аргумента в неё могут передаваться как char * так и char const * - нет проблем, приведение типа делается автоматом.
Но хочется, чтобы и возвращаемое значение было того же типа, что и передано: для char * - char *, для char const * - char const *.
Чтобы не было необходимости делать: char const *s; ... ; s = (char const *)StrFunc(s);
Понятно, что можно объявить inline функцию, в которой сделать приведение к нужному типу. Но тогда, с низким уровнем оптимизации, функция не будет заинлайнена.
Или можно перегрузить эту функцию с другим типом аргумента/возвращаемого значения - опят будут две функции, выполняющие одно действие.
Может есть более элегантное решение?
Как бы так объявить алиас для данной функции с другим типом аргумента/возвращаемого значения?
scifi
Цитата(jcxz @ May 10 2017, 13:28) *
Имеется функция:
char *StrFunc(char const *str) {}
Внутри она использует str именно как char const * (только чтение) и потом возвращает например: str+N (N - типа int).

Функция принимает указатель на const, возвращает указатель по сути на тот же объект, но убирает const. Это нечестно.

То есть должно быть 2 функции:
Код
char* f1(char* s);
const char* f2(const char* s);

Причём код внутри одинаковый. Немного жаль, что в языке нет элегантного способа это разрулить. Предложение такое:
Код
const char* f2(const char* s);
#define f1(s) (char*)f2(s)

Проверка типов сохраняется, всё работает. Логично?
Сергей Борщ
QUOTE (jcxz @ May 10 2017, 13:28) *
Понятно, что можно объявить inline функцию, в которой сделать приведение к нужному типу. Но тогда, с низким уровнем оптимизации, функция не будет заинлайнена.
Не используйте столь низкие уровни оптимизации.

QUOTE (scifi @ May 10 2017, 13:38) *
Немного жаль, что в языке нет элегантного способа это разрулить.
Смотря в каком языке. В плюсах есть, называется перегрузка функций. Можно объявить две функции с одинаковым именем но разными типами параметров. В зависимости от типа передаваемого параметра будет подставлен вызов соответствующей функции. В общем надо переходить на плюсы, даже если писать на них в стиле обычных Сей, просто постепенно добавляя использование все новых и новых плюсовых плюшек.
jcxz
Цитата(scifi @ May 10 2017, 12:38) *
Причём код внутри одинаковый. Немного жаль, что в языке нет элегантного способа это разрулить.

Я об этом и говорю.

Цитата(scifi @ May 10 2017, 12:38) *
Предложение такое:
Код
const char* f2(const char* s);
#define f1(s) (char*)f2(s)

Проверка типов сохраняется, всё работает. Логично?

Плохое решение. Две имени (функций) выполняющие одно и то же действие. И придётся каждый раз думать - какое имя подставить.

Цитата(Сергей Борщ @ May 10 2017, 13:41) *
Не используйте столь низкие уровни оптимизации.

А как жить тогда? Ведь уже при уровне "Medium" не производится inlining, а при более высоких - отладка практически невозможна. sad.gif(

Цитата(Сергей Борщ @ May 10 2017, 13:41) *
Смотря в каком языке. В плюсах есть, называется перегрузка функций.

Это я знаю (см. исходное сообщение). И это ведёт опять к тому-же - или два раза одно и то же тело описывать или вызывать через inline одно общее тело.
Вот если-б можно было один раз описать, а для второго списка аргументов использовать что-то типа typedef wink.gif
typedef Func1 Func2;
novikovfb
Цитата(jcxz @ May 10 2017, 15:52) *
Вот если-б можно было один раз описать, а для второго списка аргументов использовать что-то типа typedef wink.gif
typedef Func1 Func2;

шаблоны (template) позволяют такие выкрутасы, но ИМХО, это еще более корявая реализация, чем двойное тело функции.
scifi
Цитата(jcxz @ May 10 2017, 14:52) *
Плохое решение. Две имени (функций) выполняющие одно и то же действие. И придётся каждый раз думать - какое имя подставить.

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

Цитата(jcxz @ May 10 2017, 14:52) *
А как жить тогда? Ведь уже при уровне "Medium" не производится inlining, а при более высоких - отладка практически невозможна. sad.gif(

Как жить? Жить и не тужить. Можно подумать, от того, что функция не будет заинлайнена, небо упадёт на землю. Это всё вредные предрассудки, избавляйтесь от них.
jcxz
Цитата(scifi @ May 10 2017, 14:19) *
Как жить? Жить и не тужить.

Тут есть тема (Не)доработки языков программирования, которые хотели ли бы вы
наверное надо туда тему перекинуть - пусть обмозгуют, обжуют biggrin.gif
Сергей Борщ
QUOTE (jcxz @ May 10 2017, 14:52) *
А как жить тогда? Ведь уже при уровне "Medium" не производится inlining, а при более высоких - отладка практически невозможна. sad.gif(
Отлаживайтесь в окне дизассемблера. Отладка в этом окне возможна при любом уровне оптимизации. Я делаю именно так.

QUOTE (jcxz @ May 10 2017, 14:52) *
или вызывать через inline одно общее тело.
Это лучше. А что, у вашего компилятор нет _Pragma("inline=forced")?

k155la3
Цитата(novikovfb @ May 10 2017, 14:56) *
шаблоны (template) позволяют такие выкрутасы, но ИМХО, это еще более корявая реализация, чем двойное тело функции.

Шаблон таже двойная (энная) реализация. И ябы не сказал что корявая.
По крайней мере для простых применений (какие я пользую) - самое-то.
Шаманъ
Цитата(jcxz @ May 10 2017, 13:28) *
Имеется функция:
char *StrFunc(char const *str) {}
Внутри она использует str именно как char const * (только чтение) и потом возвращает например: str+N (N - типа int).

Вариант sm.gif:
Код
#define func(str) ((str)+N)


Это была больше шутка, впрочем иногда вполне годная wink.gif.

Самое близкое к описанному Вами поведение даст _Generic (стандарт С1х), как-то так:
Код
#define StrFunc(str)  _Generic((str), const char*: (const char*)__StrFunc(str), char*: __StrFunc(str))

Т.е. Вашу функцию делаете в виде char* __StrFunc(const char* str), а потом везде вызываете макрос StrFunc. Поведение будет точно такое, как Вы писали:
Цитата
Но хочется, чтобы и возвращаемое значение было того же типа, что и передано: для char * - char *, для char const * - char const *.
Чтобы не было необходимости делать: char const *s; ... ; s = (char const *)StrFunc(s);


Еще можно сделать макросы с использованием typeof или __auto_type (расширения GCC).
esaulenka
Что-то не понял, почему нельзя возвращать указатель на константу? Тогда оно "само" приведётся к неконстанте, если потребуется.

Код
char const *StrFunc(char const *str)
{
    return str + 5;
}

int main(void)
{
    const char str1[] = "Hello, world!\n";
    char str2[] = "Hello, world!\n";
    
    const char *res1 = StrFunc (str1);
    char *res2 = StrFunc (str2);
    
    printf(res1);
    printf(res2);
    return 0;
}



UPDATE. Чёрт, опять часть варнингов потерялась...
Код
warning: initialization discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
     char *res2 = StrFunc (str2);

Да-а, беда. Плюсы с перезагрузкой ждут Вас :-)
demiurg_spb
https://gcc.gnu.org/onlinedocs/gcc-4.4.7/gc...r-Builtins.html

__builtin_constant_p
jcxz
Цитата(esaulenka @ May 11 2017, 13:36) *
Что-то не понял, почему нельзя возвращать указатель на константу? Тогда оно "само" приведётся к неконстанте, если потребуется.
...
UPDATE. Чёрт, опять часть варнингов потерялась...

Вот именно.

Цитата(esaulenka @ May 11 2017, 13:36) *
Да-а, беда. Плюсы с перезагрузкой ждут Вас :-)

Во-первых - перегрузка.
И я писал в исходном сообщении, что это нежелательный путь, так как по сути - будут две одинаковые функции. Тогда уж лучше использовать вариант с inline одной из них.

Цитата(demiurg_spb @ May 11 2017, 16:37) *
__builtin_constant_p

У меня IAR sad.gif
krux
А вариант с union вам не подойдет ?
jcxz
Цитата(krux @ May 17 2017, 11:44) *
А вариант с union вам не подойдет ?

Какой? Возвращать union {char *; char const *;}; ?
Ну так задача-то была - чтобы при вызове не городить преобразований типов. А тут ещё более громоздкий вызов получается...
Сергей Борщ
QUOTE (jcxz @ May 17 2017, 10:09) *
Во-первых - перегрузка.
И я писал в исходном сообщении, что это нежелательный путь, так как по сути - будут две одинаковые функции. Тогда уж лучше использовать вариант с inline одной из них.
Что мешает объединить? Т.е. перегруженная функция, которая просто является inline-const-оберткой для второй? Кроме принципиального нежелания переименовывать исходник .c->.cpp других препятствий не видно. Видно стойкое желание колоться, плакать, но продолжать кушать кактус.
jcxz
Цитата(Сергей Борщ @ May 17 2017, 13:09) *
Что мешает объединить? Т.е. перегруженная функция, которая просто является inline-const-оберткой для второй? Кроме принципиального нежелания переименовывать исходник .c->.cpp других препятствий не видно. Видно стойкое желание колоться, плакать, но продолжать кушать кактус.

Я уже писал выше, что. То что inline-тся оно будет только при уровне оптимизации High.
Ничего страшного конечно, но ведь само inline - оно по любому опционально для компилятора. Т.е. - он может в любом случае понаделать лишних переходов.
scifi
Цитата(jcxz @ May 17 2017, 14:21) *
Ничего страшного конечно, но ведь само inline - оно по любому опционально для компилятора. Т.е. - он может в любом случае понаделать лишних переходов.

Патологический перфекционизЬм детектед! Срочно вызывайте санитаров biggrin.gif
Arlleex
Под рукой нет компилятора, но, на мой взгляд, было бы хорошо подумать над передачей не указателя, а указателя на указатель:
Код
int Func(const char **str)

В качестве параметров, опять же, либо char*, либо const char* - приведется автоматически, а в теле изменяете *str. В возвращаемом значении возвращаете, получилось сдвинуть или вылезли за границу строки... Оно?

P.S. Только щас обратил внимание на дату сообщения... Сорри sm.gif
jcxz
Цитата(Arlleex @ Jan 11 2018, 21:48) *
Под рукой нет компилятора, но, на мой взгляд, было бы хорошо подумать над передачей не указателя, а указателя на указатель:

И сделать текст исходника ещё более громоздким? И получить более громоздкий скомпилированный код?
Если было например:
char const *s; ... ; s = (char const *)StrFunc(s + 5);
то теперь будет ещё более громоздкая запись с сохранением в промежуточную переменную перед вызовом и чтением из неё - после.
Получим гораздо более развесистый текст. А уж код какой получится!.... Вместо простого ADDS R0, Rx, #5 перед вызовом, теперь будет: выделение места на стеке + запись в стек (s+5) + чтение из памяти внутри функции + удаление стекового фрейма после вызова функции - команд получается в несколько раз больше.
Для просмотра полной версии этой страницы, пожалуйста, пройдите по ссылке.
Invision Power Board © 2001-2025 Invision Power Services, Inc.