LINUX.ORG.RU

c++ Вопросик с лямбда

 ,


0

2

Продолжаю изучать с++ ) Загвоздка с лямбда


class Process {
public:

  int (*onReady)(const char*);  
  std::string outpurBuffer;
}

Process *proc = new Process();
proc->onReady = [](const char* reason) {
  std::string = proc->outpurBuffer;
  return 1;
};


Не работает, говорит что локальную переменную нельзя. Гуглю соответственно «c++ local variable in lambda», первый пример указывает что нужно в скобки квадратные передавать Captured variables

// Local Variables
std::string msg = "Hello";
int counter = 10;
// Defining Lambda function and
// Capturing Local variables by Value
auto func = [msg, counter] () {
                          //...
                          };

ок, делаю
proc->onReady = [proc](const char* reason) {
  std::string s = proc->outpurBuffer;
  return 1;
};

Снова ошибка, но уже с приведением типов. Что не так?

Кстати сразу другой вопросик, чем отличается
const char* reason;
const char *reason;

Есть ли разница? Компилятору я смотрю без разницы, то и то проглатывает

★★★★

Последнее исправление: gobot (всего исправлений: 1)

proc->onReady = [proc](const char* reason) -> int {
  std::string s = proc->outpurBuffer;
  return 1;
};

int (*onReady)(const char*); што енто вообще такое?

bhfq ★★★★★
()
Последнее исправление: bhfq (всего исправлений: 1)
Ответ на: комментарий от bhfq

Это в классе Process колбэк. А что не так?

gobot ★★★★
() автор топика

Продолжаю изучать с++

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

anonymous
()

int (*onReady)(const char*); это указатель на функцию.

А это лямбда:

[proc](const char* reason) {
  std::string s = proc->outpurBuffer;
  return 1;
};

Это разные типы, Всё правильно тебе говорит компилятор.

почитай про std::function

fsb4000 ★★★★★
()

Есть ли разница? Компилятору я смотрю без разницы, то и то проглатывает

Я тебе по секрету скажу, компилятору и так будет норм:

const const const char const const const *reason;

А на твой вопрос, нет, разницы нет.

и про положение *, тоже разницы нет

const char* reason;
const char * reason;
const char *reason;
fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 1)
Ответ на: комментарий от fsb4000

С лямбда такое нельзя сделать, передать ее указатель? Буду std::function смотреть, спасибо

gobot ★★★★
() автор топика
Ответ на: комментарий от fsb4000

Прикольно ) А почему везде по разному пишут, ведь звездочка применяется к reason, а не char? *reason вроде правильней?

gobot ★★★★
() автор топика
Ответ на: комментарий от gobot

ведь звездочка применяется к reason, а не char?

Да.

char const* a,b;

тут указатель только a.

b просто char const.

Но если только по одной переменной объявлять, то для кого-то легче думать, что char const* это тип. Поэтому и пишут * ближе к типу, а не к имени переменной.

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 2)
Ответ на: комментарий от gobot

аноним вообще-то прав. Бежать задавать вопросы на форумах вместо изучения всяких cppreference.com - мягко говоря, дурной тон.

Lrrr ★★★★★
()
Ответ на: комментарий от fsb4000

Но у меня в некоторых местах указывают на обычные функции, а я ещё хочу с лямбда скрестить. Нужны разные колбеки?

Вот так хочу


proc->onReady  = callback_lambla;
proc->onReady  = real_function;

gobot ★★★★
() автор топика
Ответ на: комментарий от Lrrr

аноним вообще-то прав. Бежать задавать вопросы на форумах вместо изучения всяких cppreference.com - мягко говоря, дурной тон.

С таким подходом форумы вообще не нужны, все ответы можно найти в Интернете.

Не трогайте ТС'а, нормальный у него вопрос.

Kroz ★★★★★
()
Ответ на: комментарий от Lrrr

А что, если я 1 тему создаю в день, сильно ваш трекер засирается, как тут говорил один анон? Просто интересно в чем проблема

Я кстати читаю конечно cppreference.com и кучу прочего. Если бы я в форуме спрашивал по каждой мелочи то через каждые 5 минут постил )

gobot ★★★★
() автор топика
Ответ на: комментарий от fsb4000

Ооо класс! Изменил всего лишь определение
Теперь можно с лямбда и с обычной ф-ей


class Process {
public:

  std::function<int(const char* data)> onReady;
  std::function<void(const char* data)> onClosed;  
  std::function<void(const char* data)> onData;
}

proc->onReady  = [](const char* reason) {};
proc->onReady = real_func;


Спасибо!

gobot ★★★★
() автор топика
Ответ на: комментарий от gobot

да. Обычный указатель 8 байт.

А std::function больше 8 байт, фактический размер может различаться на разных стандартных библиотеках.

Вот на clang libc++ 48 байт…

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 1)
Ответ на: комментарий от fsb4000

Чем это может быть хуже? Разница в 40 байт? Программа в памяти будет на 40 байт больше занимать? ))

gobot ★★★★
() автор топика
Ответ на: комментарий от gobot

Программа в памяти будет на 40 байт больше занимать? ))

Если судить по твоему классу, то уже 120 байт :)

class Process {
public:

  std::function<int(const char* data)> onReady;
  std::function<void(const char* data)> onClosed;  
  std::function<void(const char* data)> onData;
}

и кроме оперативной памяти есть кеш процессора. Он поменьше будет…

Не, std::function норм.

Я просто хотел показать, что у гибкости std::function есть и недостатки…

fsb4000 ★★★★★
()
Последнее исправление: fsb4000 (всего исправлений: 1)

В принципе тебе уже все расписали, замечу лишь, что так как лямбда захватывает указатель на proc, при каждом копировании или перемещении proc лямбды придется пересоздавать.

Siborgium ★★★★★
()

Почему, кстати, просто не сделать методы структуре вместо коллбеков? Некоторые API принимают указатели на методы, для остальных можно с помощью std::mem_fn коллбек сделать из метода.

struct ProcessData {
    std::string msg;
    int counter;
};

struct Process {
    // или std::atomic<std::shared_ptr<ProcessData>>
    // или просто указатели на локальные переменные, если они точно не выйдут из области видимости
    std::shared_ptr<ProcessData> data; 
    std::string outputBuffer;

    int onReady(const char*) {
        std::string s = this->outputBuffer; // зачем?
        return 1;
    }
    void onClosed(const char*) {
        // ...
    }
    void onData(const char*) {
        // ...
    }
};

Siborgium ★★★★★
()
Ответ на: комментарий от fsb4000

Понятно. Слушай раз уж тут, подскажи

buffSize = 10;
char buf[buffSize];
memset(buf,0,sizeof(buf));


имеем изначальный массив нулевых, ок
[0]: 0 '\0'
[1]: 0 '\0'
[2]: 0 '\0'
[3]: 0 '\0'
[4]: 0 '\0'
[5]: 0 '\0'
[6]: 0 '\0'
[7]: 0 '\0'
[8]: 0 '\0'
[9]: 0 '\0'


sizeof(buf) = 10 //ok
strlen(buf) = 0 //ok


затем буфер заполняет функция(вывод ffmpeg) и получается
[0]: 102 'f'
[1]: 102 'f'
[2]: 109 'm'
[3]: 112 'p'
[4]: 101 'e'
[5]: 103 'g'
[6]: 32 ' '
[7]: 118 'v'
[8]: 101 'e'
[9]: 114 'r'


у же получаю не ОК )
sizeof(buf) == 10 //
strlen(buf) == 44 //Откуда 44?


Смотрим доки по strlen, длинна определяется пока не будет обнаружен нулевой символ. Но его тут нет. По какому алгоримту тогда рассчитывается длинна?

Потом, когда этот буфер передаешь в другую функцию
static int onBuffer(const char *data){}


в ней этот буфер уже вот такой
strlen(data) = 44
sizeof(data) = 8 //почему 8, было же 10


и дебагер видит строку уже не как массив, а как нечто. Ну с этим ладно, может визуализирует как то по своему, но почему длинна 44 и чем она таким заполяется какими то ММММ
"ffmpeg verММММММММММММММММММММММММММММММММММ"


Далее, для теста я сделал
char test[10] = "123456789";
strlen(test) == 9; //верно

и есть нулевой в конце
[0]: 49 '1'
[1]: 50 '2'
[2]: 51 '3'
[3]: 52 '4'
[4]: 53 '5'
[5]: 54 '6'
[6]: 55 '7'
[7]: 56 '8'
[8]: 57 '9'
[9]: 0 '\0'

gobot ★★★★
() автор топика
Ответ на: комментарий от Siborgium

Я вроде не перемещаю никуда. Объект создается и потом удаляется через delete. Потом снова создается\удаляется ну и т.д.

gobot ★★★★
() автор топика
Ответ на: комментарий от Siborgium

Да не знаю, сделал уж, хоть как то вышло )

методы структуре вместо коллбеков

Мне нужны именно колбеки. Т.е. обратная связь. А как правильней?

gobot ★★★★
() автор топика
Ответ на: комментарий от gobot

Нагуглил таки
https://stackoverflow.com/questions/38499311/how-is-the-strlen-calculated-for...

Неопределенное значение имеет. Но почему всегда 44 загадка. Наверное от размера буфера зависит. Заметил если буфер 30 то длинна строки 72. Хотя по суди это и не строка считается, а просто массив символов

gobot ★★★★
() автор топика
Последнее исправление: gobot (всего исправлений: 1)
Ответ на: комментарий от gobot

А как правильней

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

имеем изначальный массив нулевых, ок

Ты на С пишешь, или на С++? Создание массива с длиной, не известной во время компиляции на стеке – это С.

Но его тут нет. По какому алгоримту тогда рассчитывается длинна?

Длина рассчитывается по простому алгоритму – strlen идет по памяти, пока не наткнется на нулевой байт, в твоем случае выходя за границы массива. Для таких случаев нужно использовать strnlen, передавая туда bufSize, чтобы такого не происходило. Как вариант, можно дописать в конец буфера нулевой байт самому.

Siborgium ★★★★★
()

Шутка

Кстати сразу другой вопросик, чем отличается const char* reason; const char *reason;

Есть в расположении пробела.
По секрету скажу, можно и так const char * reason;

В моих исходниках /это лишь дело вкуса/ всегда такой стиль: const char *reason;

Владимир

anonymous
()
Ответ на: комментарий от Siborgium

твоем случае выходя за границы массива

а, вот оно что. Ясно, спасибо. По идее может до бесконечности идти? Да, я прочитал уже, может даже сегфолт быть... Да..ох уж этот си. Надо все на std::string делать

gobot ★★★★
() автор топика
Ответ на: комментарий от Siborgium

Да, кстати, нужно наверное все таки нулевой символ вставлять в конце, а то я смотрю

std::string str(buf);

строка создается «ffmpeg verММММММММММММММММММММММММММММММММММ», вместо просто «ffmpeg ver».
Как его добавить? buf[buffSize] = '{0}'? Компилятор ругается «buffer 'buf' of size 10 bytes will be overrun»

gobot ★★★★
() автор топика
Ответ на: комментарий от Siborgium

Пробовал так. Мне не подходит.
1) ReadFile будет ждать вечно, пока поток не закроешь, потом высрет все сразу
2) ReadFile читает пайп всегда с начала

короче мне нужно непрерывно читать stdout и не закрывать пайп. Я через PeekNamedPipe делаю

gobot ★★★★
() автор топика
Ответ на: комментарий от gobot

buf[buffSize] = ‘{0}’? Компилятор ругается «buffer ‘buf’ of size 10 bytes will be overrun»

Индекс массива с нуля начинается, …
https://habr.com/ru/post/495444/ Массивы в C++

Владимир

anonymous
()
Ответ на: комментарий от gobot

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

Как его добавить?

Добавить его можно как buf[buffSize - 1] = '\0'. Но вообще, лучше вообще не конструировать строку из буфера, а сделать как я указал выше. Если (все же) оставишь как есть, то вместо добавления нулевого символа (напомню, это затрет последний символ в буфере) лучше делать

std::string str(buf, bread);
Siborgium ★★★★★
()
Ответ на: комментарий от anonymous

А если твердо? Или забанят?

Твердо забанят

anonymous
()
Ответ на: комментарий от gobot
  1. ReadFile будет ждать вечно, пока поток не закроешь, потом высрет все сразу

Странно. Я не специалист в WinAPI, но судя по документации так не должно быть. Ладно, не суть важно, пример заключался именно в том, как в строку можно вставлять буфер, не заморачиваясь с нулевыми символами.

  1. ReadFile читает пайп всегда с начала

Не понял, что вы имеете в виду. Если вам нужно пропустить какое-то количество байт с начала (скажем, первых 100 байт), то вы просто читаете в один и тот же буфер, пока сумма всех считанных байт не составит 100, и только потом приступаете к сохранению прочитанного.

Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Вот и я думаю, затрет ведь. А со строкой хороший вариант, спасибо

gobot ★★★★
() автор топика
Ответ на: комментарий от fsb4000

Это что за компилятор?

Это рабочий код:

#include <iostream>

struct A {
  void (*f)(const char *);
};

int main() {
  using namespace std;

  A *a = new A;
  a->f = [](const char *s) { cout << "Lambda called " << s << endl; };

  const char st[] = "abc";
  a->f(st);
}
rumgot ★★★★★
()
Ответ на: комментарий от Aswed

Два чая топикстартеру.

… и пирожок

anonymous
()
Ответ на: комментарий от Siborgium

Кстати можно функции-члены присваивать и в std::function:

#include <functional>
#include <iostream>

using namespace std;

class SomeClass {
public:
  SomeClass() {}
  void m() { cout << "SomeClass::m called" << endl; }
};

int main() {
  SomeClass *someClassObject = new SomeClass;
  function<void(SomeClass *)> f = &SomeClass::m;
  f(someClassObject);
}
rumgot ★★★★★
()
Ответ на: комментарий от rumgot

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

Siborgium ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.