Хабр Курсы для всех
РЕКЛАМА
Практикум, Хекслет, SkyPro, авторские курсы — собрали всех и попросили скидки. Осталось выбрать!
Я хочу оставлять меньше багов, потому желаю строгую типизацию, качественные сообщения об ошибках и статический анализ кода. Хочу упростить поиск ошибок, а, следовательно, хорошие отладочные средства и динамический анализ.А Си-то тут причём?)
Синтаксис указателей на функции понимают два с половиной специалиста
void (*variable)(int, int);
вовзращаемое_значение (*имя_переменной)(аргументы);
typedef вовзращаемое_значение (*имя_типа)(аргументы);
using имя_типа = вовзращаемое_значение (*)(аргументы);
вовзращаемое_значение (Класс::*имя_переменной)(аргументы);
typedef вовзращаемое_значение (Класс::*имя_типа)(аргументы);
using имя_типа = вовзращаемое_значение (Класс::*)(аргументы);
Правда с вызовом чуточку по другому:Clazz foo;
(foo.*some_variable)(parameters);
Clazz *bar = new Clazz;
(bar->*some_variable)(parameters);
а если его не использовать, получится дикая смесь из скобок, запятых и имен типов, которую невозможно ни толком прочитать, ни толком понять.
Плюс еще нужно о соглашении о вызовах не забыть, если предполагается, что программа будет использоваться на голом железеМне кажется, что это к C, как таковому, имеет сильно посредственное отношение: трудно стандартизовать то, что может поменяться. Вдруг кто напишет OS, где будет принято аргументы в функцию передавать через один: через стек, через регистр, через стек, через регистр, или передавать адрес возврата первым аргументом. Или последним. Или в середине. Ну и тяжёлое наследие x86. На x86_64 как бы не так много вариантов оставили. Ну и другие платформы есть.
Но тот факт, что указатели на функции в их нынешнем виде мало кто понимает — вижу на работе стабильно раз в месяц.Вот тут, похоже, я встретился с тем, чего я не знаю или не совсем понимаю. По сути, какое отношение имеет указатель на функцию с соглашениями на вызов? Ну и примеры недопонимания можно привести, без вреда NDA и прочему POPI.
#include "Uefi.h"
// Define SIO DXE protocol GUID
#define EFI_CRSIO_DXE_PROTOCOL_GUID \
{ 0xc38bdbd, 0x6f6b, 0x49ae, 0x99, 0x5a, 0x1d, 0x6c, 0xc2, 0x9d, 0x95, 0xa1 }
// Extern the GUID for protocol users.
extern EFI_GUID gEfiCrSioDxeProtocolGuid;
// Forward reference for ANSI C compatibility
typedef struct _EFI_CRSIO_DXE_PROTOCOL EFI_CRSIO_DXE_PROTOCOL;
// Define each protocol interface function and the respective function pointer type
typedef
UINT16
(EFIAPI *CRSIO_DXE_GET_INDEX) ( //Если вот здесь забыть EFIAPI, ничего не сломается, т.к. функция не имеет параметров.
VOID //А вот если бы они были, а компилятором оказался GCC, то он бы их положил в RDI, RSI...
); //а функция их ждет в RCX, RDX..., т.е. на вид все хорошо, а по факту в регистрах мусор вместо параметров
UINT16
EFIAPI // А если забыть его вот здесь, не забыв выше, будет почти то же самое, только упадет все в другом месте
CrSioGetIndex(
VOID
);
// Define protocol data structure
typedef struct _EFI_CRSIO_DXE_PROTOCOL
{
CRSIO_DXE_GET_INDEX CrSioGetIndex;
} EFI_CRSIO_DXE_PROTOCOL;
почти Lisp :-)
А теперь то же самое, но для функции,
Consider the signal() function from the C standard:
extern void (*signal(int, void(*)(int)))(int);
Perfectly obscurely obvious — it's a function that takes two arguments, an integer and a pointer to a function that takes an integer as an argument and returns nothing, and it (signal()) returns a pointer to a function that takes an integer as an argument and returns nothing.
ручное управление памятью, которое почти никто не умеет делать правильно
Ваши правила не работают.
Два простых правила, чтобы забыть о проблемах с ручным управлением памятью:
1) удаляем память там же где и создаем
2) следующая вещь, которая пишется после new — это delete
Ну ладно, один конкретный пример: нужно учитывать циклические ссылки и держать весь путь мигрированя указателя, чтобы не допустить цикличности. По сути это слабо отличается от необходимости держать такой же путь для обычного указателя, чтобы предсказуемо его удалить.
Ну ладно, один конкретный пример: нужно учитывать циклические ссылки и держать весь путь мигрированя указателя, чтобы не допустить цикличности. По сути это слабо отличается от необходимости держать такой же путь для обычного указателя, чтобы предсказуемо его удалить.
Меня не интересует красивая реалистичная картинка, зато я забочусь о производительности.А нахрена производительность в играх, кроме как на красивую картинку?:)
Я согласен в чем-то с автором
Весь opengl api это С, shaders тоже по сути C-like syntax
class cA{
public:
foo();
};
class cB{
cA* m_a;
bar(){
m_a->foo();
}
}
By definition, it is the responsibility of the object, not the external code, to 'on-demand' select the procedural code to run/execute in response to a method call, typically by looking up the method at run time in a table associated with the object.
Object-oriented programming (OOP) is a programming paradigm based on the concept of «objects», which are data structures that contain data, in the form of fields, often known as attributes; and code, in the form of procedures, often known as methods.
Основной принцип ООП — это то, что мы работем не с функциями и данными, а с данными, заключенными в объект ВМЕСТЕ с методами
Основной принцип ООП — это то, что мы работем не с функциями и данными, а с данными, заключенными в объект ВМЕСТЕ с методами, которые эти данные обрабатывают.
Например, в Python инкапсуляция отсутствует в принципе
Нет, определение ООП-языков не в этом.
In programming languages, encapsulation is used to refer to one of two related but distinct notions, and sometimes to the combination[1][2] thereof:
A language mechanism for restricting access to some of the object's components.[3][4]
A language construct that facilitates the bundling of data with the methods (or other functions) operating on that data.[5][6]
Так зачем использовать зоопарк языков, когда все можно писать на одном.
И да, компиляция статическая, т.к. происходит проверка типов по всем модулям.
Когда мне нужен ребилд — я просто ставлю его на ночь.
Когда мне нужен ребилд — я просто ставлю его на ночь.
пихает реализацию в H
Отдельно интересно, как автор решает проблему шаблонов в хидере в своих играх на plain C.
Отдельно интересно, как автор решает проблему шаблонов в хидере в своих играх на plain C.
Если же этого не хватает, то можно поместить исходники проекта на RAM диск и компиляция станет практически мгновенной.
Да, С++ сложнее С.
Он настолько сложен, что ни один(или все такие есть такой?) компилятор не поддерживает его стандарт полностью.
Например, я пишу на С++ игры, но я некоторые возможности С++ не знаю и не изучаю. Например, я почти не ориентируюсь в шаблонах и без документации не смогу написать даже простого. Просто потому, что шаблоны практически не применяются при программировании игр.
Это именно вы их не применяете безотносительно области вашей разработки.
Если бы вы разрабатывали что-либо другое, то точно так же не применяли бы их.
public sealed class MarketDataStream<T,X,Y> : IDisposable, ILoggerAttached, INamed
where X: IStreamHeader
where Y: IStreamFeedbackHeader
template <class T, IStreamHeader X, IStreamFeedbackHeader Y>
class MarketDataStream : public IDisposable, public ILoggerAttached, public INamed { }
public interface IStreamHeader
{
Guid Tag { get; set; }
long Size { get; set; }
long Heartbeat { get; set; }
int DataOffset { get; set; }
bool Choked { get; set; }
}
template <class T>
class MarketDataStream : public IDisposable, public ILoggerAttached, public INamed
{
// конструктор
MarketDataStream(IStreamHeader X, IStreamFeedbackHeader Y)
{
// ...
}
}
Ясно, это я еще помню как override в С++ раньше не было, тоже удивлен был.
Формально реализован, по факту встречаются баги сильно чаще, чем в clang. Причём, чаще это мешающие баги — clang скорее акцептит невалидный код, тогда как gcc реджектит валидный.
Как у них там с экспортом темплейтов из модулей, кстати?
Сфера применения знака / (косая черта) — научная и деловая речь. Он употребляется в следующих функциях.
1. В функции, близкой к союзам и и или, как знак альтернативности понятий или обозначения единого сложного понятия.
Это неприменимо при указании названия языков.
void fn(const int *x)
{
int y;
x = &y; // error
*x = y; // error
}
const(int) *x

Это большая свобода и большая ответственность, но уж лучше так, чем сражаться с языком когда доходишь до предела его возможностей и натыкаешься на невидимую стену из багов или ограничений производительности.
Лучше я сам буду рулить памятью чем сборщик мусора будет фризить мою прогу когда ему вздумается, и мне виднее когда память выделять а когда освобождать.
В случае Си мне всегда понятно что оно делает и как, никаких махинаций за ширмой, никакого волшебства, нюансы конечно есть но их довольно мало в сравнении с языками которые на себя берут заметно больше.
Что до линукса, то смотря на их забавные попытки, по сути, реализовать C++ на Си в той же VFS, то там тоже не видно причин не использовать С++А зачем там плюсы? Всё равно rtti и exceptions придётся отключать для ядра, плюс развлечения по борьбе с mangling, что приведёт код к Си-подобному. Хотя шаблоны были бы крайне приятны.
Почему я пишу игры на C (да, на C)