не работает инициализация класса через new

b707
Offline
Зарегистрирован: 26.05.2017

Подскажите, почему вот такой код работает

My_Class a(A,B,C);

void setup() {
a.init();
}
void loop() {
a.run();
}

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

My_Class *a;

void setup() {
a = new  My_Class(A,B,C);
a->init();
}
void loop() {
a->run();
}

может при такой инициализации какой секрет есть?

Уточняю - внутри конструктора есть выделение динамического массива через malloc. пробовал переносить malloc из конструктора в init() (прочитал, что конструктор при непосредственном вызове и через new работает немного по разному... не помогло)

Прошу прощения, что выкладываю псевдокод - реальный  слишком лохматый, раскидан по шести исходным фалам... Если по этому псевдо ничего сказать нельзя - так и скажите. Буду рыть сам.

 

rkit
Offline
Зарегистрирован: 23.11.2016

нельзя

b707
Offline
Зарегистрирован: 26.05.2017

rkit пишет:

нельзя

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

Вот реально в огромной программе меняю только эту инициализацию класса. Эй богу не вру :) Первый код работает, второй нет.

Ладно, спасибо, покопаюсь сам.

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

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

rkit
Offline
Зарегистрирован: 23.11.2016

Разница есть, конечно. В первом случае класс живет на стеке, во втором в куче.

b707
Offline
Зарегистрирован: 26.05.2017

asam пишет:

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

я сам знаю что должно работать :)

написал выше - много раз пользовал и то и то.

Блин, значит где в остальном коде очень хитрый косяк...

b707
Offline
Зарегистрирован: 26.05.2017

rkit пишет:

Разница есть, конечно. В первом случае класс живет на стеке, во втором в куче.

а это направление для поиска. "Нерабочесть" второго варианта выглядит так, будто динамический буфер не выделился или выделился неверно...

asam
asam аватар
Offline
Зарегистрирован: 12.12.2018

b707 пишет:

а это направление для поиска. "Нерабочесть" второго варианта выглядит так, будто динамический буфер не выделился или выделился неверно...

А возвращаемый malloc-ом указатель ты на NULL проверяешь?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Ну, хоть конструктор-то покажите! И вообще, сделайте маленький пример. Или маленькие работают? Если так то надо "лохматый" смотреть.

b707
Offline
Зарегистрирован: 26.05.2017

ЕвгенийП пишет:

Ну, хоть конструктор-то покажите! И вообще, сделайте маленький пример. Или маленькие работают? Если так то надо "лохматый" смотреть.

показываю:

Конструктор

DMD::DMD(byte _pin_A, byte _pin_B, byte _pin_nOE, byte _pin_SCLK, byte panelsWide, byte panelsHigh, SPIClass _spi )
  : pin_DMD_A(_pin_A), pin_DMD_B(_pin_B), pin_DMD_nOE(_pin_nOE), pin_DMD_SCLK(_pin_SCLK),
    DisplaysWide(panelsWide), DisplaysHigh(panelsHigh), SPI_DMD(_spi)
{
  bool dbuf = false;
#if (USE_DUAL_BUFFER == 1)
  dbuf = true;
#endif

  DisplaysTotal = DisplaysWide * DisplaysHigh;
  row1 = DisplaysTotal << 4;
  row2 = DisplaysTotal << 5;
  row3 = ((DisplaysTotal << 2) * 3) << 2;
  mem_Buffer_Size = DMD_RAM_SIZE_BYTES * DisplaysTotal;
  uint16_t allocsize = (dbuf == true) ? (mem_Buffer_Size * 2) : mem_Buffer_Size;
  matrixbuff[0] = (uint8_t *)malloc(allocsize);
  // If not double-buffered, both buffers then point to the same address:
  matrixbuff[1] = (dbuf == true) ? &matrixbuff[0][mem_Buffer_Size] : matrixbuff[0];
  backindex = 0;
  bDMDScreenRAM = matrixbuff[backindex]; // -> Back buffer
  front_buff = matrixbuff[1 - backindex]; // -> Front buffer

#if defined(__STM32F1__)
  pin_DMD_CLK = SPI_DMD.sckPin();
  pin_DMD_R_DATA = SPI_DMD.mosiPin() ;
  pinMode(pin_DMD_nOE, PWM);
  SPI_DMD.begin();

#if defined(DMD_USE_DMA)
  dmd_dma_buf = (byte *)malloc(DisplaysTotal * DMD_DMA_BUF_SIZE);
  rx_dma_buf = (byte *)malloc(DisplaysTotal * DMD_DMA_BUF_SIZE);
  spiDmaDev = DMA1;
  if (SPI_DMD.dev() == SPI1) {
    spiTxDmaChannel = DMA_CH3;
    spi_num = 1;
  }
  else {
    spiTxDmaChannel = DMA_CH5;
    spi_num = 2;
  }
#endif
#endif

  digitalWrite(pin_DMD_A, LOW); //
  digitalWrite(pin_DMD_B, LOW); //
  digitalWrite(pin_DMD_CLK, LOW); //
  digitalWrite(pin_DMD_SCLK, LOW);  //
  digitalWrite(pin_DMD_R_DATA, HIGH); //

  pinMode(pin_DMD_A, OUTPUT); //
  pinMode(pin_DMD_B, OUTPUT); //
  pinMode(pin_DMD_CLK, OUTPUT); //
  pinMode(pin_DMD_SCLK, OUTPUT);  //
  pinMode(pin_DMD_R_DATA, OUTPUT);  //
  bDMDByte = 0;
}

Деструктор (добавил только когда появились проблема, раньше не было

DMD::~DMD()
{
  free(matrixbuff[0]);
#if defined( DMD_USE_DMA )
  free(dmd_dma_buf);
  free(rx_dma_buf);
#endif
}

члены и методы (сокращено)

//The main class of DMD library
class DMD
{
  public:
    //Instantiate the DMD

    DMD(byte _pin_A, byte _pin_B, byte _pin_nOE, byte _pin_SCLK, byte panelsWide, byte panelsHigh, SPIClass _spi );
    ~DMD();
    virtual void init(uint16_t scan_interval = 2000);



    /* many methods scipped */


    uint8_t spi_num = 0;
    uint16_t WIDTH, HEIGHT;
  protected:
    // pins
    byte pin_DMD_nOE;   // active low Output Enable, setting this low lights all the LEDs in the selected rows. Can pwm it at very high frequency for brightness control.
    byte pin_DMD_A;
    byte pin_DMD_B;
    byte pin_DMD_CLK;    // is SPI Clock if SPI is used
    byte pin_DMD_SCLK;
    byte pin_DMD_R_DATA ;   // is SPI Master Out if SPI is used

    SPIClass SPI_DMD;

    //Mirror of DMD pixels in RAM
    byte *bDMDScreenRAM;
    uint16_t mem_Buffer_Size;
    uint8_t         *matrixbuff[2];
    volatile uint8_t backindex = 0;
    volatile boolean swapflag;
    volatile uint8_t *front_buff;

#if defined(__STM32F1__)
    dma_channel  spiTxDmaChannel;
    dma_dev* spiDmaDev;
    uint8_t *dmd_dma_buf;
    uint8_t *rx_dma_buf;
#endif

    //Display information
    byte DisplaysWide;
    byte DisplaysHigh;
    byte DisplaysTotal;
    int row1, row2, row3;

    //scanning pointer into bDMDScreenRAM
    volatile byte bDMDByte;
};

собственно, полный код библиотеки есть на Гитхабе , правда там версия чуть старше, но в целом текст совпадает

b707
Offline
Зарегистрирован: 26.05.2017

asam пишет:

А возвращаемый malloc-ом указатель ты на NULL проверяешь?

нет, не проверял. Теперь, конечно, проверю... Но вроде памяти дофига, статические переменные занимают 14% ОЗУ, динамические - еще от силы процентов пять...

Кроме того, в первом-то варианте все работает, значит памяти хватает?

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Не, так не пойдёт. У конструктора 7 параметров без умолчательных значений, а вызове в первом посте - всего три. Он бы не скомпилировался.

Давайте полный пример, а то каша какая-то.

b707
Offline
Зарегистрирован: 26.05.2017

тогда полный пример вот

https://github.com/board707/DMD_STM32/blob/master/examples/STM32F1/dmd_cyr_chars/dmd_cyr_chars.ino

Только есть нюанс - на этом примере я не проверял, проявляется ли тут описанная проблема.

Я понимаю, что это хамство по вашей классификации, так что давайте я для начала сам попробую :)

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

Давайте :-)

rkit
Offline
Зарегистрирован: 23.11.2016

А нормально вообще SPIClass передавать  по значению?

b707
Offline
Зарегистрирован: 26.05.2017

rkit пишет:

А нормально вообще SPIClass передавать  по значению?

согласен, может не слишком эффективно. Но он же небольшой. Хотя надо будет попробовать по ссылке.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

Первая можно сказать фундаментальная ошибка это b707 положил большой болт на правило пяти. А это значит что компилятор будет делать в таких ситуациях как ему удобно. Не хотите их писать, так закройте их компиляцию.

https://riptutorial.com/ru/cplusplus/example/5421/правило-пяти

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

qwone пишет:

Первая можно сказать фундаментальная ошибка это b707 положил большой болт на правило пяти. 

Круто!

b707
Offline
Зарегистрирован: 26.05.2017

ЕвгенийП пишет:

b707 пишет:

давайте я для начала сам попробую :)

Давайте :-)

попробовал, пример с гитхаба (из стабильного релиза библиотеки) работает в обоих вариантах, из беты - во втором случае выводит на матрицу "молоко" вместо картинки. Значит я что-то в последнем релизе напортачил...

Будем искать.

b707
Offline
Зарегистрирован: 26.05.2017

qwone пишет:

Первая можно сказать фундаментальная ошибка это b707 положил большой болт на правило пяти. А это значит что компилятор будет делать в таких ситуациях как ему удобно. Не хотите их писать, так закройте их компиляцию.

https://riptutorial.com/ru/cplusplus/example/5421/правило-пяти

про правило пяти прочитал, спасибо (впервые слышу, но в принципе все понятно и логично). А вот фразу про "не хотите их писать - закройте компиляцию" - не понял. Что мне нужно закрыть?

b707
Offline
Зарегистрирован: 26.05.2017

Чудеса

После двух пересборок глюк пропал, теперь и мой изначальный пример работает в обоих вариантах.

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

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

b707 пишет:
про правило пяти прочитал, спасибо (впервые слышу, но в принципе все понятно и логично). А вот фразу про "не хотите их писать - закройте компиляцию" - не понял. Что мне нужно закрыть?

SomeString(char) = delete; // любое использование этого конструктора приведет к ошибке

Объявить их в классе и написать delete. Это наследие осталось когда классы были структурами и компилятор их создавал автоматически. Точнее и сейчас создаст ,если не закрыть их создание. Конечно можно сказать что пользователь с дуру не начнет создавать класс копированием исходного. Но ли. Пользователи бывают очень умными или очень дурными и компилятор начнет создавать то что не закрыто.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

b707 пишет:
ХЗ что это было...
действительно ХЗ, но как вариант (чисто в порядке бреда), когда сложные объекты инициализируются в глобальном контексте (когда инициализация имеет побочный эффект) нередко встречается глюк (отличие в поведении от того, что происходит, если их в программе ручками инициализировать), связанный с тем, что порядок такой инициализации не определён.

Например, допустим вот здесь кнопка в момент включения питания НЕ нажата. Неясно какое именно значение в строке №11 получит m_State, т.к. не определено кто первым инициализируется: thePin или theButton. Пример глупый - только, чтобы выпятить проблему.

struct CPin {
	CPin(const uint8_t _pin) {
		pinMode(_pin, INPUT_PULLUP);
	}
};

struct CButton {
	bool m_State;

	CButton(const uint8_t _pin) {
		m_State = ! digitalRead(_pin); // ХЗ !!!!
	}
};

CPin thePin(5);
CButton theButton(5);

void setup(void) {}
void loop(void) {}

Более того, порядок их инициализации может измениться при очередной компиляции от любого чиха в исходнике.

А вот в таком варианте всё чётко и однозначно.

struct CPin {
	CPin(const uint8_t _pin) {
		pinMode(_pin, INPUT_PULLUP);
	}
};

struct CButton {
	bool m_State;

	CButton(const uint8_t _pin) {
		m_State = ! digitalRead(_pin); // ХЗ !!!!
	}
};

CPin * thePin;
CButton * theButton;

void setup(void) {
	thePin = new CPin(5);
	theButton = new CButton(5);
}
void loop(void) {}

Поэтому, когда нужна сложная инициализация с побочными эффектами, лучше использовать что-то однозначное. Через указатели, конечно, слишком круто - обычно сложную инициализацию делают в специальном методе типа "init()" или "begin()", а не в конструкторе, а уж методы вызывают в правильном порядке.