Перейти к содержанию
    

Плавный переход C -> C++ под МК

1 час назад, Forger сказал:

язык тут не причем, это - всего лишь ИНСТРУМЕНТ...

Да, но... стандартная библиотека ведь написана так, что любой дурак вряд ли что-то там поломает, и оно будет работать всегда и везде одинаково. И я пытаюсь написать кусок кода, который будет работать тоже везде и одинаково. Но получается, что это тупо невозможно...
 

2 часа назад, Sh@dow сказал:

Как вообще на нем писать низкоуровневые вещи?

Знать что компилятор гарантирует а что нет.

В вашем случае sizeof(Base) == sizeof(Derived) не обязано быть верным. Так как компилятор имеет право подравнивать адреса для

ускорения времени доступа.

Ничего он не будет подравнивать при определенных условиях. Если Вы имели в виду оптимизацию Empty Base Optimization - то это не про то.

На самом деле моя задача вполне выполнима, и гарантии, все-таки, есть, достаточно лишь выполнить некоторое количество правил при определении классов.

https://stackoverflow.com/questions/7160901/why-is-c11s-pod-standard-layout-definition-the-way-it-is

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

7 minutes ago, Arlleex said:

И я пытаюсь написать кусок кода, который будет работать тоже везде и одинаково. Но получается, что это тупо невозможно...

Всегда и везде одинаково - это утопия ))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

41 минуту назад, Arlleex сказал:

На самом деле моя задача вполне выполнима, и гарантии, все-таки, есть, достаточно лишь выполнить некоторое количество правил при определении классов.

На самом деле тут проблема не только в наследовании, но ещё и в битовых полях. А вот они не переносимы по определению.
 

Цитата

The following properties of bit-fields are implementation-defined:

  • The value that results from assigning or initializing a signed bit-field with a value out of range, or from incrementing a signed bit-field past its range.
  • Everything about the actual allocation details of bit-fields within the class object.
  • For example, on some platforms, bit-fields don't straddle bytes, on others they do.
  • Also, on some platforms, bit-fields are packed left-to-right, on others right-to-left.

Multiple adjacent bit-fields are usually packed together (although this behavior is implementation-defined):

 

 

12 часов назад, Forger сказал:

вспомнил, почему отказался от "внутри класса", он не поддерживает auto, которую очень часто вставляю по коду:

Чушь какая. Очень даже поддерживает.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

При виртуальном наследовании:

class A {
	int x;
};

class B: virtual A {
};
class C: virtual A {
};

class D: B, C {
};

классы B и C будут больше, чем класс A, из-за добавления таблицы виртуальных методов vtable.

Виртуальное наследование позволяет в показанном случае включать класс A только один раз в класс D. Если бы этого виртуального наследования не было, то в класс D было бы включено два разных класса A - через класс B и через класс C. Это пример так называемого "ромбовидного" наследования.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

29 minutes ago, AHTOXA said:

Чушь какая. Очень даже поддерживает.

какая версия компилятора?  у меня c++17

вот такие поля в классах не работают:

	auto kvalue = 0;
error: 'auto' not allowed in non-static class member

	const auto kvalue = 0;
error: 'auto' not allowed in non-static class member

	constexpr auto kvalue = 0;
error: non-static data member cannot be constexpr; did you intend to make it static?

 

работает только так:

int kvalue = 0;
const int kvalue = 0;
static const int kvalue = 0;

static const auto kvalue = 0;
static constexpr auto kvalue = 0;

 

 

т.е. так не работает:

const auto kvalue = 0;

а вот так прекрасно проходит:

const int kvalue = 0;

так что auto имеет свои особенности при объявлении в полях класса

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

13 минут назад, Forger сказал:

какая версия компилятора?  у меня c++17

Я отвечал на вот это сообщение:

12 часов назад, Forger сказал:

вспомнил, почему отказался от "внутри класса", он не поддерживает auto, которую очень часто вставляю по коду:

static constexpr auto a = 40

 

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 hour ago, AHTOXA said:

Я отвечал на вот это сообщение:

это пример как эта auto как раз работает, но такая динная конструкция не всегда применима, да и порой нужен объект в памяти, размер и тип которого сам компилятор считает

Например "auto a = 0.35f" работать не будет, хотя тут логично что тип float (по типу литерала).

а явное "float a = 0.35f" конечно уже работает, но выходит масло масляное

 

даже "static const auto a = 0.35f" не работает, надо вместо const вставлять constexpr, только тогда перестает ругаться,

но это слишком длинная запись: "static constexpr auto" вместо просто "float". Мне не годится такое "нагромождение" ))

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

1 час назад, AHTOXA сказал:

На самом деле тут проблема не только в наследовании, но ещё и в битовых полях. А вот они не переносимы по определению.

Угу. И меня даже полностью бы устроило контролируемое размещение в памяти (без зазоров и выравниваний), не важно в каком порядке - лишь бы одинаково. Код мой работал бы. Но стандарт не гарантирует про битовые поля вообще ничего. Вот логика ж у людей там - впилить что-то такое, на однозначность которого полагаться никогда нельзя. Я порой даже остерегаюсь концепции "проверил у себя в компиляторе и забыл" - т.к. возможно от разных ключей компиляции или при некоторых видах оптимизации поведение может быть совсем различным. Ужос((

Забил я на эти битовые поля, и решил сделать класс, в котором приватный член имеет обычный тип u32. А конструкторы и мембер-функции оперируют с его частями Си-шными сдвигами и масками. Более надежного метода придумать сложно. Но когда отнаследовался от базового класса, в котором этот самый единственный u32, задался вопросом - а гарантирует ли стандарт, что наследуемый класс не "раздуется" и не станет занимать больше памяти, чем базовый? Да, я понимал про виртуальное наследование и функции, но у меня этого нет - и гуры на стековерфлоу (с отсылками к стандарту) утверждали, что нет, гарантий нет. Но более опытный индус привел выдержку из стандата по поводу "классов со стандартной компоновкой" и все встало на места (почти). Интересна строчка

Цитата

A standard-layout class is a class that:

  • has no non-static data members of type non-standard-layout class (or array of such types) or reference,
  • has no virtual functions (10.3) and no virtual base classes (10.1),
  • has the same access control (Clause 11) for all non-static data members,
  • has no non-standard-layout base classes,
  • either has no non-static data members in the most derived class and at most one base class with non-static data members, or has no base classes with non-static data members, and
  • has no base classes of the same type as the first non-static data member.


Т.е.

class A {
  int a;

  public:
    int b;
};

это уже не standard-layout class. Почему...

Т.е. гарантировано и однозначно занимающий ровно 4 байта базовый класс и его потомки либо нельзя написать, либо для этого надо перелопатить весь стандарт и молиться:sad:

1 час назад, EdgeAligned сказал:

При виртуальном наследовании:

У меня нет виртуального наследования. Я просто хочу классы с данными и методами, и иметь обычное одиночное наследование, чтобы не писать кучу одинакового кода обработки полей. И я хочу гарантий, что все наследники, как и базовый класс, будут иметь одинаковое количество байтов в памяти. Простейшая вещь, но с которой можно, мягко говоря, а*ереть, пока напишешь.

Оказывается, если я переопределяю дефолтный конструктор MyClass() {}, то этот класс больше не является POD-типом, соответственно он имеет не стандартную компоновку со всеми вытекающими - т.е. нет гарантий, что при наследовании не вылезет приколов с занимаемой памятью.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

4 minutes ago, Arlleex said:

Т.е. гарантировано и однозначно занимающий ровно 4 байта базовый класс и его потомки либо нельзя написать

"pragma pack" попробуйте

у меня помогало, работала одинаково на совершенно разных платформах

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

2 минуты назад, Forger сказал:

"pragma pack" попробуйте

у меня помогало, работала одинаково на совершенно разных платформах

Ну, только так...

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

8 часов назад, Arlleex сказал:

Т.е. гарантировано и однозначно занимающий ровно 4 байта базовый класс и его потомки либо нельзя написать, либо для этого надо перелопатить весь стандарт и молиться:sad:

Можно, но это будет просто struct с функциями. В целом нам, эмбеддерам, проще - проверил на целевом компиляторе, и порядок. При смене платформы, возможно придётся повторить проверки, но это небольшая потеря на фоне остальных дел, которые надо сделать при смене платформы:)

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

25 минут назад, EdgeAligned сказал:

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

При передаче по каналам связи, или когда мне нужно было гарантировать, чтобы объект какой-то структуры занимал не более стольких-то байтов, в Си я мог сделать это элементарно... В C++ нюансов целая пачка, у меня сомнения даже в том, что есть ли хоть один человек на земле, который на 100% уверен в том, что он написал (вернее, будет ли это работать верно или ему повезло, что компилятор не намудрил). Вот, типа, хорошо - я ушел от битовых полей в классах/структурах (понятное дело, что они практически на 99% implementation-defined), но решил воспользоваться прелестями плюсов в части ООП - упрятал данные в класс. По мере обрастания функционала класса (даже банальное наследование от него) синтаксис рано или поздно потребует явного определения дефолтного конструктора, пусть даже пустого. Потом оказывается, что определяя конструктор класса, класс перестает отвечать требованиям POD-типов и, соответственно, "слетает гарантия" на признаки POD, такие как стандартный лэйаут полей в памяти. Т.е. заверений в том, что наследники без явно описанных полей не будут внезапно больше (или меньше! такое тоже может быть) размером - компилятор не дает. Стиль "отладил и забыл" как-то постепенно распадается на молекулы. Приходится обмазываться с ног до головы static_assert() во всех "опасных" местах (да знать бы о них еще заранее, а то так - вечно случайно узнаю).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

9 minutes ago, Arlleex said:

При передаче по каналам связи, или когда мне нужно было гарантировать, чтобы объект какой-то структуры занимал не более стольких-то байтов, в Си я мог сделать это элементарно... В C++ нюансов целая пачка

независимо от языка у меня структуры занимали ровно столько, сколько было задано, pragma решает

а при чем тут компилятор? приведите пример разницы в си и плюсах одной и той же структуры, где она ведет себя по-разному

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

11 минут назад, Forger сказал:

независимо от языка у меня структуры занимали ровно столько, сколько было задано, pragma решает

а при чем тут компилятор? приведите пример разницы в си и плюсах одной и той же структуры, где она ведет себя по-разному

Я акцентирую внимание чуть на другом: компилятор выполняет требования основополагающих стандартов, в котором (для Си-плюс-плюс) всяких неоднозначностей и поведений, отдаваемых на откуп реализаторам компиляторов, вагон и маленькая телега. Поэтому программирование становится похоже на хождение по минному полю - вроде дорожку протоптал, следы запомнил. Но шаг влево - и бабах.

Пример. Посмотрите сначала, что будет в i, j, а потом раскомментируйте строчку Foo() {}

struct Foo
{
    //Foo() {}
    int x;
    char y;
};

struct Bar : public Foo
{
    char z[3];
};

int main()
{
    int i = (unsigned)sizeof( Foo );
    int j = (unsigned)sizeof( Bar );
}

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
К сожалению, ваш контент содержит запрещённые слова. Пожалуйста, отредактируйте контент, чтобы удалить выделенные ниже слова.
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...