Комментарии 65
Но объясните, пожалуйста, зачем так мучиться, почему нельзя использовать C++? К тому же, оба перечисленных вами компилятора его поддерживают.
-15
Не везде он применим. Например некоторые микроконтроллеры. И в linux kernel с плюсами не пустят.
PS
Каждый язык имеет свою конкретную область применения. Не стоит пихать С++ и Java во все дырки подряд, в наши дни это чревато.
PS
Каждый язык имеет свою конкретную область применения. Не стоит пихать С++ и Java во все дырки подряд, в наши дни это чревато.
+24
Ну, например, коллеги требуют набор функций для микроконтроллеров, а корпоративный стандарт заставляет использовать чистый С.
+3
Мне кажется, лучше было бы показать, как похожие приёмы используются в Linux Kernel или, к примеру, в FreeBSD. Там обширная поддержка List/Queue/Tree для различных типов в виде макросов на C.
+3
Как бы, GNU кучу литературы и примеров поставляет. Любой человек, полезший в ядро их найдёт. Это всё-таки специфичные вещи, которые не должны перемешиваться со школьными или университетскими знаниями. Представляете, что будет, если скзать человеку, изучающий Си в течение месяца-двух, что в ядре линукса активно используют goto? Это должно приходить с опытом, постепенно, а не статьями на хабре.
+2
Я бы хотел это услышать в рамках своего институтского курса, ведь не просто так я в ВУЗ пошел учится. МФТИ как-никак. Но да, конечно, нельзя эту информацию допускать до новичков. Проблема.
0
С каких пор ФизТех стал прогерским вузом?;)
Ещё пару лет назад там всё-таки думать учили, а не прогать;)
Прогать все учились сами по себе, в тех областях, которые им интересны.
Ещё пару лет назад там всё-таки думать учили, а не прогать;)
Прогать все учились сами по себе, в тех областях, которые им интересны.
0
Я думаю, учебное заведение должно способствовать собсветнно обучению студента в интересующей его области. А так да, на физтехе и по сей день учат думать. Правда нависла огромная угроза — ЕГЭ. Я сам ЕГЭшник, я гораздо ниже уровнем тех, кто поступал по внутренним экзаменам. Преподаватели тому очень не рады — уровень падает на глазах. Что с этим можно делать в рамках закона — никто не знает :(.
0
Вы это сегодня узнали — необходимости это включать в ВУЗовский курс нет. Знание разлиных алгоритмов, их применений, ограничений гораздо важнее. Если у Вас есть понимание что такое шаблон и вы владеете Си, то легко решите определённую задачу, но эту задачу надо получить/сформулировать. Фундаментальное знание всегда выигрывает перед практикой, т.к. практику всегда можно освоить, просто потратив время.
+1
Есть много вещей, которые трудно научиться «самому по себе» и часто требуется четкое объяснение, чего на форумах не всегда получается выяснить (особенно узкие места фреймворков, библиотек, структуры языка в целом). Вы видимо не с ФИВТ'а?
0
Представляете, что будет, если скзать человеку, изучающий Си в течение месяца-двух, что в ядре линукса активно используют goto?
«Вы так говорите, как будто это что-то плохое».
+3
Я вообще удивляюсь, как можно писать на С код, который работает надёжно, и при этом не использовать goto. Это же либо миллиарды функций будет, либо столько отступов, что надо будет ставить 2 монитора рядом, и код будет на правом из них :)
0
я тоже об этом думаю, когда читаю стандарты MISRA, которые как раз запрещают goto и одновременно гарантируют определённый уровень отсутствия ошибок.
Аргументируют возможностями статической проверки кода компилятором и прочими lint'ами.
До сих пор думаю, как выделять несколько ресурсов и корректно их освобождать в ошибочных и не очень ситуациях без goto.
Аргументируют возможностями статической проверки кода компилятором и прочими lint'ами.
До сих пор думаю, как выделять несколько ресурсов и корректно их освобождать в ошибочных и не очень ситуациях без goto.
+1
Да там ничего сложного, всё построено на макросе
#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;
};
Дальше всё как на лекции про связные списки.
#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;
};
Дальше всё как на лекции про связные списки.
0
На мой взгляд в статье показан интересный trick, с включением одного и того же хедера с разными параметрами через определения макросов. Этот же способ используется, например, в boost::function для определения классов-функций с количеством параметров 0 до 10 (а зарезервировано аж до 50).
0
1. Boost.Preprocessor
2. Уж лучше наверное на m4 или на python сгенерировать всё что нужно…
2. Уж лучше наверное на m4 или на python сгенерировать всё что нужно…
-4
Судя по коду хидер должен называться не template.h а templates.h
да и в функции main не хватает знаков пунктуации…
да и в функции main не хватает знаков пунктуации…
+1
Спасибо, исправил.
ПС: всегда завидовал таким людям, которым для обнаружения ошибок не нужно нажимать кнопку «build»
ПС: всегда завидовал таким людям, которым для обнаружения ошибок не нужно нажимать кнопку «build»
0
В джавских IDE необходимость нажимать Build для обнаружения ошибок скорее исключение, чем правило, например :)
0
Ну а так чисто, собирать пример пробовали? Просто я думал что допущены синтаксические ошибки у вас из-за вашего вольного перевода, но и в оригинале статьи они есть так же.
Решил попробовать собрать под линухой. выдало ошибки еще как раз на используемой фиче.
Наверняка вы уже заметили, что в этом заголовочном файле отсутствует типовая конструкция для защиты от повторного включения #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
Я допускаю что у меня руки кривые. Но учитывая ошибки, которые нашел просто просматривая исходник, закралось сомнение…
Решил попробовать собрать под линухой. выдало ошибки еще как раз на используемой фиче.
Наверняка вы уже заметили, что в этом заголовочном файле отсутствует типовая конструкция для защиты от повторного включения #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
Я допускаю что у меня руки кривые. Но учитывая ошибки, которые нашел просто просматривая исходник, закралось сомнение…
+1
Нет, не пробовал. Но идеей пользовался лет 8 назад, не ожидал подвоха. Сейчас проверю, поправлю исходники. Пока не буду статью в черновики прятать…
Наличие ошибки сборки подтверждаю.
Наличие ошибки сборки подтверждаю.
-1
Нашел еще одну ошибку в исходной статье
В файле sum_as_template.c вместо
int TEMPLATE(sum,T) (int n, T *a, T *b)
должно было быть
void TEMPLATE(sum,T) (int n, T *a, T *b)
С этими изменениями должно собираться нормально.
В файле sum_as_template.c вместо
int TEMPLATE(sum,T) (int n, T *a, T *b)
должно было быть
void TEMPLATE(sum,T) (int n, T *a, T *b)
С этими изменениями должно собираться нормально.
0
В микроконтроллерах всему этому находится самое широкое применение. Например, в STM8.
Мне нужно поменять скважность ШИМ на каком-то таймере. Код выглядит так:
Но таймеров несколько (TIM2, TIM3, ...), и у каждого таймера несколько каналов (CCR1, CCR2, ...).
Тогда беру и пишу шаблон:
Вызываю так:
Мне нужно поменять скважность ШИМ на каком-то таймере. Код выглядит так:
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);
+5
а вот пресловутые стандарты MISRA запрещают использовать нетривиальные макросы. Компилятору и статическим тулзам труднее будет опознать, где же вы там накосячили. И даже разыменование указателей запрещают в макросы прятать.
0
Это ужасно. Но прочитал с интересом, вспомнил С и баловство с макросами.
+7
Я вот фильм «Звонок» вспомнил. Казалось бы — ничто не предвещало беды, а потом вдруг на тебя из монитора такая хрень лезет!
+8
С другой стороны ясно, что у Ц есть ещё «порох в пороховницах и ягоды в ягодицах».
Чрезвычайно элегантный язык для прострела ноги.
«На Си можно писать объектно», однозначно.
Моя первая любовь, так скэть…
(То, что влюбился в компы через фортран и васик — не то. В Ц я влюбился именно как в язык сам по себе).
Чрезвычайно элегантный язык для прострела ноги.
«На Си можно писать объектно», однозначно.
Моя первая любовь, так скэть…
(То, что влюбился в компы через фортран и васик — не то. В Ц я влюбился именно как в язык сам по себе).
+3
#define sum(n,a,b){size_t i; for(i = 0; i < (n); i++) (a)[i] += (b)[i];}
Совместимо с C99 и, вероятно, C90.
+4
Советую автору посмотреть в сторону ключевого слова _Generic из C11 стандарта
Вызов cbrt(x) будет транслирован в вызов cbrtl(x), cbrt(x) или cbrtf(x) в зависимости от типа x
#define cbrt(X) _Generic((X), long double: cbrtl, \
default: cbrt, \
float: cbrtf)(X)
Вызов cbrt(x) будет транслирован в вызов cbrtl(x), cbrt(x) или cbrtf(x) в зависимости от типа x
+7
-----стерто----
-6
НЛО прилетело и опубликовало эту надпись здесь
Прекратите насиловать несчастную сишку. Для изврата есть C++, ява и прочие извращённые языки.
-1
Мне кажется упростит всё это дело:
В такой формулировке:
1. Получаем читаемую ошибку, если не объявлен T
2. В начале файла сразу видно что нужно объявить
3. Подключение всего этого дела упростится:
#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"
+4
А ныне модно называть макросы препроцессора шаблонами?
0
Лично я (лет десять назад) когда требовалась шаблонизация на С делал макросы на m4.
Этот пример выглядел бы так.
sums.h.m4:
sums.c.m4:
А вот строка для мейкфайла:
Ну и файл с определениями:
sums_template(float)
sums_template(double)
sums_template(int)
Собственно я и сейчас так часто делаю, когда надо.
Имхо препроцессор С уж очень малофункционален, приходится делать так.
С другой стороны возможностей у такого подхода намного больше чем у темплайтов.
В конце концов, почему код должны писать программисты а не роботы-кодогенераторы?
:-)
Этот пример выглядел бы так.
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)
Собственно я и сейчас так часто делаю, когда надо.
Имхо препроцессор С уж очень малофункционален, приходится делать так.
С другой стороны возможностей у такого подхода намного больше чем у темплайтов.
В конце концов, почему код должны писать программисты а не роботы-кодогенераторы?
:-)
+1
Здорово! Практического смысла мало, но как упражнение для мозга — в самый раз.
Ещё очень интересная есть задачка для раздумий: как реализовать поддержку классов в чистом C с полиморфизмом и прочими радостями.
Ещё очень интересная есть задачка для раздумий: как реализовать поддержку классов в чистом C с полиморфизмом и прочими радостями.
0
Тагда уж лучше Objective-С поковырять, там этого добра достаточно.
0
>Ещё очень интересная есть задачка для раздумий: как реализовать поддержку классов в чистом C с полиморфизмом и прочими радостями.
Как-то давно баловался таким. И наследование было, и виртуальные методы. Если будет время — могу статью забабахать, если интересно.
Как-то давно баловался таким. И наследование было, и виртуальные методы. Если будет время — могу статью забабахать, если интересно.
+2
Конечно интересно: сравнить с собственными потугами.
0
Пока я раскачивался, меня опередили: habrahabr.ru/post/155439/
P.S.: чтобы не дублировать комментарий, но поставить пользователя в известность, воспользуюсь новой фичей и упомяну silvansky :)
P.S.: чтобы не дублировать комментарий, но поставить пользователя в известность, воспользуюсь новой фичей и упомяну silvansky :)
+1
Забабахайте! Будет интересно =)
Давно что-то не было статей о базовых вещах и тонкостях разработки на C и C++.
Давно что-то не было статей о базовых вещах и тонкостях разработки на C и C++.
0
НЛО прилетело и опубликовало эту надпись здесь
В исходниках Quake 2 точно был полиморфизм на Си. Ну, в виде:
struct A {
void (*virtualMethod)(int,int);
}
0
Blut und Eisen! Руки прочь от моей милой сишечки.
Упарывайтесь шаблонами и макросами в своих ООП монстрах.
Вон!
P.S. Си жив до сих пор как раз потому, что на данный момент это один из самых легкочитаемых языков.
Самый простой способ изменить такое положение вещей — это использовать макросы.
Упарывайтесь шаблонами и макросами в своих ООП монстрах.
Вон!
P.S. Си жив до сих пор как раз потому, что на данный момент это один из самых легкочитаемых языков.
Самый простой способ изменить такое положение вещей — это использовать макросы.
0
Пока что к шаблонам это имеет примерно такое же отношение, как советский калькулятор к современному ноутбуку. Ждём пост о метапрограммировании на макросах only!
+3
Кстати, в стандарте C99 допускается заменить такой код
int i;
for(i = 0; i < n; i++) a[i] += b[i];
на
for(int i = 0; i < n; i++) a[i] += b[i];
int i;
for(i = 0; i < n; i++) a[i] += b[i];
на
for(int i = 0; i < n; i++) a[i] += b[i];
+2
Правильно! Весь ад плюсов (не читаемость кода, сложность поддержки сторонними кодерами, не явные ошибки) в C.
0
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
Шаблоны на C. Да! На чистом С. Не С++