Pull to refresh

Comments 65

Но объясните, пожалуйста, зачем так мучиться, почему нельзя использовать C++? К тому же, оба перечисленных вами компилятора его поддерживают.
Не везде он применим. Например некоторые микроконтроллеры. И в linux kernel с плюсами не пустят.

PS
Каждый язык имеет свою конкретную область применения. Не стоит пихать С++ и Java во все дырки подряд, в наши дни это чревато.
> И в linux kernel с плюсами не пустят
ну таки и в linux kernel с такими define-ами не пустят. а в остальном — да, не везде плюсы засунешь.
А что не так с дефайнами? Дефайны как дефайны, не вижу почему их в кернел нельзя пустить.
Ну, например, коллеги требуют набор функций для микроконтроллеров, а корпоративный стандарт заставляет использовать чистый С.
Мне кажется, лучше было бы показать, как похожие приёмы используются в Linux Kernel или, к примеру, в FreeBSD. Там обширная поддержка List/Queue/Tree для различных типов в виде макросов на C.
Как бы, GNU кучу литературы и примеров поставляет. Любой человек, полезший в ядро их найдёт. Это всё-таки специфичные вещи, которые не должны перемешиваться со школьными или университетскими знаниями. Представляете, что будет, если скзать человеку, изучающий Си в течение месяца-двух, что в ядре линукса активно используют goto? Это должно приходить с опытом, постепенно, а не статьями на хабре.
Я бы хотел это услышать в рамках своего институтского курса, ведь не просто так я в ВУЗ пошел учится. МФТИ как-никак. Но да, конечно, нельзя эту информацию допускать до новичков. Проблема.
С каких пор ФизТех стал прогерским вузом?;)
Ещё пару лет назад там всё-таки думать учили, а не прогать;)
Прогать все учились сами по себе, в тех областях, которые им интересны.
Я думаю, учебное заведение должно способствовать собсветнно обучению студента в интересующей его области. А так да, на физтехе и по сей день учат думать. Правда нависла огромная угроза — ЕГЭ. Я сам ЕГЭшник, я гораздо ниже уровнем тех, кто поступал по внутренним экзаменам. Преподаватели тому очень не рады — уровень падает на глазах. Что с этим можно делать в рамках закона — никто не знает :(.
Вы это сегодня узнали — необходимости это включать в ВУЗовский курс нет. Знание разлиных алгоритмов, их применений, ограничений гораздо важнее. Если у Вас есть понимание что такое шаблон и вы владеете Си, то легко решите определённую задачу, но эту задачу надо получить/сформулировать. Фундаментальное знание всегда выигрывает перед практикой, т.к. практику всегда можно освоить, просто потратив время.
Есть много вещей, которые трудно научиться «самому по себе» и часто требуется четкое объяснение, чего на форумах не всегда получается выяснить (особенно узкие места фреймворков, библиотек, структуры языка в целом). Вы видимо не с ФИВТ'а?
Представляете, что будет, если скзать человеку, изучающий Си в течение месяца-двух, что в ядре линукса активно используют goto?

«Вы так говорите, как будто это что-то плохое».
Я вообще удивляюсь, как можно писать на С код, который работает надёжно, и при этом не использовать goto. Это же либо миллиарды функций будет, либо столько отступов, что надо будет ставить 2 монитора рядом, и код будет на правом из них :)
я тоже об этом думаю, когда читаю стандарты MISRA, которые как раз запрещают goto и одновременно гарантируют определённый уровень отсутствия ошибок.

Аргументируют возможностями статической проверки кода компилятором и прочими lint'ами.

До сих пор думаю, как выделять несколько ресурсов и корректно их освобождать в ошибочных и не очень ситуациях без goto.
Даже если надо всего лишь 3 кусочка памяти выделить и туда чего нибудь записать — уже без goto неудобно.
Да там ничего сложного, всё построено на макросе
#define container_of(ptr, type, member) ({ \
const typeof( ((type *)0)->member ) *__mptr = (ptr); \
(type *)( (char *)__mptr — offsetof(type,member) );})

А в свою структуру надо добавить поле типа
struct list_head {
struct list_head *next, *prev;
};

Дальше всё как на лекции про связные списки.
На мой взгляд в статье показан интересный trick, с включением одного и того же хедера с разными параметрами через определения макросов. Этот же способ используется, например, в boost::function для определения классов-функций с количеством параметров 0 до 10 (а зарезервировано аж до 50).
1. Boost.Preprocessor
2. Уж лучше наверное на m4 или на python сгенерировать всё что нужно…
Судя по коду хидер должен называться не template.h а templates.h
да и в функции main не хватает знаков пунктуации…
Спасибо, исправил.

ПС: всегда завидовал таким людям, которым для обнаружения ошибок не нужно нажимать кнопку «build»
В джавских IDE необходимость нажимать Build для обнаружения ошибок скорее исключение, чем правило, например :)
Ну а так чисто, собирать пример пробовали? Просто я думал что допущены синтаксические ошибки у вас из-за вашего вольного перевода, но и в оригинале статьи они есть так же.

Решил попробовать собрать под линухой. выдало ошибки еще как раз на используемой фиче.

Наверняка вы уже заметили, что в этом заголовочном файле отсутствует типовая конструкция для защиты от повторного включения #ifndef HEADER_H #define HEADER_H… #endif. И это неспроста, и мы потом еще к этому моменту вернемся

sum_as_template.c:5:1: error: conflicting types for 'sum_float'
sum_as_template.h:5:1: note: previous declaration of 'sum_float' was here
In file included from all_possible_sums.c:14:0:
sum_as_template.c:5:1: error: conflicting types for 'sum_double'
sum_as_template.h:5:1: note: previous declaration of 'sum_double' was here
In file included from all_possible_sums.c:20:0:
sum_as_template.c:5:1: error: conflicting types for 'sum_int'
sum_as_template.h:5:1: note: previous declaration of 'sum_int' was here

Я допускаю что у меня руки кривые. Но учитывая ошибки, которые нашел просто просматривая исходник, закралось сомнение…
Нет, не пробовал. Но идеей пользовался лет 8 назад, не ожидал подвоха. Сейчас проверю, поправлю исходники. Пока не буду статью в черновики прятать…

Наличие ошибки сборки подтверждаю.
Нашел еще одну ошибку в исходной статье

В файле sum_as_template.c вместо

int TEMPLATE(sum,T) (int n, T *a, T *b)

должно было быть

void TEMPLATE(sum,T) (int n, T *a, T *b)

С этими изменениями должно собираться нормально.
В микроконтроллерах всему этому находится самое широкое применение. Например, в STM8.

Мне нужно поменять скважность ШИМ на каком-то таймере. Код выглядит так:

TIM2->CCR1H = (u8)(uPulse >> 8);
TIM2->CCR1L = (u8)(uPulse);


Но таймеров несколько (TIM2, TIM3, ...), и у каждого таймера несколько каналов (CCR1, CCR2, ...).

Тогда беру и пишу шаблон:

#define ChangePulse_PWM(timer, channel, pulse) \
  timer##->CCR##channel##H = (u8)(pulse >> 8); \
  timer##->CCR##channel##L = (u8)(pulse);


Вызываю так:
ChangePulse_PWM(TIM2, 3, uPulse); 
а вот пресловутые стандарты MISRA запрещают использовать нетривиальные макросы. Компилятору и статическим тулзам труднее будет опознать, где же вы там накосячили. И даже разыменование указателей запрещают в макросы прятать.
Спасибо за упоминания MISRA. У нас на предприятии мы задумываемся над введением стандартов кодирования на Си. MISRA может послужить основой, возьмём оттуда то, что не слишком параноидально.
сам занимаюсь этой задачей (фоново), могу предложить пдфок по теме (всякие embedded c coding standards и т.п.).
Было бы неплохо
Это ужасно. Но прочитал с интересом, вспомнил С и баловство с макросами.
Я вот фильм «Звонок» вспомнил. Казалось бы — ничто не предвещало беды, а потом вдруг на тебя из монитора такая хрень лезет!
С другой стороны ясно, что у Ц есть ещё «порох в пороховницах и ягоды в ягодицах».
Чрезвычайно элегантный язык для прострела ноги.
«На Си можно писать объектно», однозначно.

Моя первая любовь, так скэть…
(То, что влюбился в компы через фортран и васик — не то. В Ц я влюбился именно как в язык сам по себе).
#define sum(n,a,b){size_t i; for(i = 0; i < (n); i++) (a)[i] += (b)[i];}

Совместимо с C99 и, вероятно, C90.
Да-да, будет кушаться больше памяти на код. Тогда совет ниже от Veliant.
Советую автору посмотреть в сторону ключевого слова _Generic из C11 стандарта

#define cbrt(X) _Generic((X), long double: cbrtl, \
                              default: cbrt, \
                              float: cbrtf)(X)

Вызов cbrt(x) будет транслирован в вызов cbrtl(x), cbrt(x) или cbrtf(x) в зависимости от типа x
Это прекрасно! Осталось лишь дождаться когда не только Keil и Tasking начнут соответствовать стандарту C11, но еще и CodeWarrior.
да, я вот когда анонсировали редактирование комментариев, подумал — а почему же уж сразу удаление не сделать, с такими же ограничениями. А то ошибся вкладкой — и вот! печаль.
UFO landed and left these words here
Прекратите насиловать несчастную сишку. Для изврата есть C++, ява и прочие извращённые языки.
Мне кажется упростит всё это дело:
#ifndef T
#error T should be defined
#else
…
#undef T
#endif

В такой формулировке:
1. Получаем читаемую ошибку, если не объявлен T
2. В начале файла сразу видно что нужно объявить
3. Подключение всего этого дела упростится:
#include "templates.h"
// на всякий пожарный
#ifdef T
#undef T
#endif
#define T float
#include "sum_as_template.h"
// а тут знаем, что undef сделан
#define T double
#include "sum_as_template.h"
#define T int
#include "sum_as_template.h"
А ныне модно называть макросы препроцессора шаблонами?
Лично я (лет десять назад) когда требовалась шаблонизация на С делал макросы на m4.
Этот пример выглядел бы так.

sums.h.m4:
dnl $Id$
m4_divert(0)
#pragma GCC dependency "sums.c.m4"
m4_divert(-1)

m4_define([sums_template],[
//! computes a:=a+b where a and b are two $1 arrays of length n
void sums_$1(int n, $1 *a, $1 *b);
])

m4_divert(0)
m4_include(sums.list)


sums.c.m4:
dnl $Id$
m4_divert(0)
#pragma GCC dependency "sums.c.m4"

m4_divert(-1)

m4_define([sums_template],[
void sums_$1(int n, $1 *a, $1 *b)
{
    /* computes a:=a+b where a and b are two $1 arrays of length n */
    int i;
    for(i=0;i<n;i++) a[[i]]+=b[[i]];
}
])

m4_divert(0)
m4_include(sums.list)


А вот строка для мейкфайла:

%.c: %.c.m4
        m4 -R ../scripts/m4sugar.m4f $< >$@

%.h: %.h.m4
        m4 -R ../scripts/m4sugar.m4f $< >$@



Ну и файл с определениями:

sums_template(float)
sums_template(double)
sums_template(int)


Собственно я и сейчас так часто делаю, когда надо.
Имхо препроцессор С уж очень малофункционален, приходится делать так.
С другой стороны возможностей у такого подхода намного больше чем у темплайтов.

В конце концов, почему код должны писать программисты а не роботы-кодогенераторы?
:-)
Здорово! Практического смысла мало, но как упражнение для мозга — в самый раз.

Ещё очень интересная есть задачка для раздумий: как реализовать поддержку классов в чистом C с полиморфизмом и прочими радостями.
Тагда уж лучше Objective-С поковырять, там этого добра достаточно.
Старые компиляторы C++ были лишь трансляторами в C, и они делали именно это.
В Objective-C всё сильно сложнее, там много функций возложено на runtime.
>Ещё очень интересная есть задачка для раздумий: как реализовать поддержку классов в чистом C с полиморфизмом и прочими радостями.

Как-то давно баловался таким. И наследование было, и виртуальные методы. Если будет время — могу статью забабахать, если интересно.
Конечно интересно: сравнить с собственными потугами.
Пока я раскачивался, меня опередили: habrahabr.ru/post/155439/

P.S.: чтобы не дублировать комментарий, но поставить пользователя в известность, воспользуюсь новой фичей и упомяну silvansky :)
Да, я как раз читаю =)
Но альтернативная реализация (ежели она у Вас сильно отличается от упомянутой) тоже была бы в тему.
Забабахайте! Будет интересно =)

Давно что-то не было статей о базовых вещах и тонкостях разработки на C и C++.
UFO landed and left these words here
В исходниках Quake 2 точно был полиморфизм на Си. Ну, в виде:
struct A {
void (*virtualMethod)(int,int);
}
Blut und Eisen! Руки прочь от моей милой сишечки.
Упарывайтесь шаблонами и макросами в своих ООП монстрах.
Вон!

P.S. Си жив до сих пор как раз потому, что на данный момент это один из самых легкочитаемых языков.
Самый простой способ изменить такое положение вещей — это использовать макросы.
Вообще, макросы в C++ пришли как наследие от C, где они и использовались достаточно широко. И разнообразная «макросовая магия», как уже замечали выше, используется в Linux Kernel, к примеру.
Пока что к шаблонам это имеет примерно такое же отношение, как советский калькулятор к современному ноутбуку. Ждём пост о метапрограммировании на макросах only!
Кстати, в стандарте C99 допускается заменить такой код

int i;
for(i = 0; i < n; i++) a[i] += b[i];

на

for(int i = 0; i < n; i++) a[i] += b[i];
И слава, что современный Objective-C базируется на С-99. Я так в своё время намучался с С-89…
Правильно! Весь ад плюсов (не читаемость кода, сложность поддержки сторонними кодерами, не явные ошибки) в C.
Как видите, «весь ад» не является языко-специфичным, а реализуется даже в чистой уютной сишечке вполне стандартными способами.
«Проблема не в сортирах, проблема в головах».
Only those users with full accounts are able to leave comments. Log in, please.