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)

Ответ на: комментарий от rumgot

Сам спросил - сам ответил.

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

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

#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

А для чего еще лор нужен?

Про метапрог вбрасывать, конечно же! И мягко троллировать Шомана насчёт его возможной тщательно скрываемой гомосексуальности.

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

strlen(buf) == 44 //Откуда 44?

strlen() считает байты до первого ‘\0’. У тебя его в твоей строке нет, он где-то дальше по стеку попадается. Вместо 44 ты можешь так же получить 127, 384, 11 и SEGMENTATION FAULT.

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

Неправильно ты ответил. Лямбда это всегда function object.(Функтор это https://en.wikipedia.org/wiki/Functor и в стандарте С++ никогда не употреблялся термин функтор, и даже в какой-то конференции представители комитета говорили, что это какие-то дауничи стали так говорить, и давайте соблюдать мировую терминологию и не выдумывать свою…). В твоём случае происходит неявное преобразование. А неявные преобразования зло.

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

https://gcc.godbolt.org/z/ev8hrE

С помощью оператора + можно затребовать явное преобразование, и будет ошибка компиляции, если есть захваченные переменные и этого невозможно сделать…

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

ты зря начал обвязку писать над утилитами ffmpeg, лучше возьми их c api, оно достаточно высокоуровневое и не страшное, сделаешь свой ffmpeg\ffplay с нужными тебе фичами

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

А неявные преобразования зло

Нет. Неявные неожиданные преобразования зло. А ожидаемые и желаемые - нет.

Неправильно ты ответил. Лямбда это всегда функтор. В твоём случае происходит неявное преобразование

Да. Признаю. Перечитал стандарт.

Просто когда нет захвата, он реализует метод как статический метод класса

А вот тут ты наврал.

C++17 Draft N4659 8.1.5.1 Closure types
The closure type for a non-generic lambda-expression with no lambda-capture has a conversion function to
pointer to function with C ++ language linkage (10.5) having the same parameter and return types as the
closure type’s function call operator.

Итого исправленный комментарий:

#include <functional>
#include <iostream>

using namespace std;

class Functor {
   public:
    Functor(){};
    void operator()() { cout << "Functor::operator()" << endl; }
};

void caller(void (*fp)()) { fp(); }

template <typename F>
void callerTemplate(F f) {
    f();
}

int main() {
    // Здесь можно присвоить лямбда-выражение указателю на функцию (или передать в качестве указателя
    // на функцию в другую функцию) потому что лямбда без списка захвата (в этом случае лямбда реализована
    // компилятором как функтор у которого есть функция преобразования в указатель на функцию с такой же сигнатурой вызова, что и
    // оператор вызова operator() данного функтора и поэтому данную лямбду можно привести к указателю на функцию):
    void (*p0)() = []() { cout << "Lambda without capture list called" << endl; };  // Ok
    caller([]() { cout << "Lambda without capture list called" << endl; });         // Ok

    int i{};
    // А в случае, когда лямбда имеет список захвата, компилятор реализует ее как функтор без функции
    // преобразования в указатель на функцию (который уже нельзя привести к указателю  на функцию):
    // void (*p1)() = [i]() { cout << "Lambda with capture list called " << i << endl; };  // Ошибка компиляции
    // caller([i]() { cout << "Lambda with capture list called " << i << endl; });         // Ошибка компиляции
    // caller(Functor());                                                                  // Ошибка компиляции

    // В случае использования шаблонов, один шаблон может принимать функциональные сущности разных типов
    // (разумеется для каждого типа будет инстанцирован свой экземпляр шаблона):
    callerTemplate([i]() { cout << "Lambda with capture list called " << i << endl; });
    callerTemplate(Functor());
}

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

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

Некоторые так делают из-за потенциального `char* const`.

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

в стандарте С++ никогда не употреблялся термин функтор, и даже в какой-то конференции представители комитета говорили, что это какие-то дауничи стали так говорить, и давайте соблюдать мировую терминологию и не выдумывать свою…)

Данный термин используется многими профессионалами (например авторы книг C++ Primer Plus - Stephen Prata, C++ Templates The Complete Guide Second Edition - David Vandevoorde Nicolai M. Josuttis Douglas Gregor, Modern C++ Programming Cookbook - Marius Bancila). Если кто-то из комитета считает эти люди даунчики - может быть не стоило так позориться. А тебе не стоило этот позор сюда тащить? Есть много терминов не закрепленных стандартом, например «Синглон Мейерса», «Resource Acquisition Is Initialization (RAII)» и т.д. однако все опытные C++ программисты знают, что это такое.

rumgot ★★★★★
()

Лизка-пипизка. Думаешь в шляпе тебя никто не узнает?

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

Ну может про «даунчики» и я добавил, но про то что стандарт никогда не использовал такого термина, и нужно использовать общую терминологию которая будет понятна любому, а не С++ программисту это точно было.

вот нашёл ту беседу на cppcon: https://imgur.com/a/wdWRoSC

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

нужно использовать общую терминологию

Кому нужно? Тебе и тому чуваку из комитета. Не согласен. Использовали термин, а тут резко кому-то он стал противным. Это мне напоминает новомодную замену терминов master->main. Термин функтор понятен всем профессионалам, даже тем, кому он не нравится. Это какая-то дикатура: тебе не нравится термин и ты начинаешь оскорблять всех кто его использует.

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

Стандарт говорит что и как преобразовывать, но он не говорит как реализовывать.

Лямбды - это синтаксический сахар. И такое

auto f = []() { return 0; }
auto i = f();

По сути эквивалентно

struct S{ static int s(){ return 0; } };
auto i = S::s();

И на то и на другое можно просто держать указатель. А такое

auto f = [=a, &b]() { return a + b; }
auto i = f();

эквивалентно этому

struct S{ int operator(){ return a + b; } const; auto a; const auto& b; };
auto i = S::s();

Здесь одним указателем не отделаешься, надо еще о времени жизни думать

Компилятор может даже так лямбды и реалировать по капотом.

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

тебе нужен ещё один байт в буфере под терминальный 0. у тебя буфер 10 байт и ффмпег пишет 10 байт, а надо буфер 11 байт чтобы ещё и 0 поместился. и читай не sizeof(buffer), а sizeof(buffer) - 1.

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

так пишут потому что * относится к переменной, а не к типу. char* c; - указатель на char, char* c, cc; - указатель на char и char. это типичная ошибка, правильно было бы char *c, *cc;. поэтому и не советуют объявлять несколько переменных в одной строке, а советуют по строке на переменную. тогда const char* c; const char cc; - сразу видно где пропущен *.

а правильно писать в том же стиле в котором остальные исходники.

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

мягко троллировать Шомана насчёт его возможной тщательно скрываемой гомосексуальности

а он разве это скрывает?

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

Анонимус, ты бы так эталонно не позорился.

Во-первых, красивое иностранное слово, которым ты решил щегольнуть, ты употребил не по делу. «Ньюфаг» — это не синоним «новичка» или «нуба», это слово, которым означают поклонника всего нового (как правило, неразборчивого).

Во-вторых, лямбды — это фича C++11, которую продолжали развивать во всех последующих стандартах. Прибавь, что до компиляторов эти новшества обычно едут несколько лет, потом программисты их начинают массово применять… Я помню, когда-то впервые увидел лямбды в коде LeechCraft (мой тогдашний g++ не понял, что это такое). Вот Дедфуд, автор LeechCraft, как раз на тот момент выступил в роли ньюфага (я не говорю, что это плохо, человеку интересно было попробовать новую взоможность языка).

В третьих, лямбды — объективно сложный материал (хотя шаблоны в самом С++, пожалуй, посложнее будут), это попытка внести в императивный язык возможность из функциональных языков.

Поэтому — отстань от новичка. Он учится, учиться незазорно. А ты показываешь свою собственную глупость. «Учебник для ньюфагов» нашёлся, блин.

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

Ха. Серия комбо, да. Кстати интересное замечание есть у Джосаттиса в книге о шаблонах: всегда ставить const после того, что мы хотим сделать константным.

Ну типа:


std::string const st;
int const i;
int const& ri;
char* const c_str;
char const * const c_str_const = nullptr;
rumgot ★★★★★
()
Последнее исправление: rumgot (всего исправлений: 2)
Ответ на: комментарий от hateyoufeel

Это я уже понял, что продолжает читать до...пока не найдет 0. Вопрос: до какого момента он ищет этот ноль? До конца астрала? Стек? Мой буфер ограничен 10 байтами. Зачем он дальше ищет? Понимаю, что нужно залезть в исходники strlen и посмотреть на код, но это займет неопределенное время

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

Очевидно, что он не смотрит на размер твоего буфера. Просто идет либо до первого встреченного в памяти \0 либо пока программа не упадет.

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

до какого момента он ищет этот ноль?

Пока не найдет, или не сегфолтнется.

Мой буфер ограничен 10 байтами. Зачем он дальше ищет?

strlen ничего не знает про длину твоего буфера. Используй strnlen, чтобы ее сообщить.

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

strlen() не знает сколькими байтами ограничен твой буфер, ты же ему не сказал. man strnlen.

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

char const * const *

Я не так писал, тут добавлена звездочка справа. Но так или иначе это указатель на константный указатель на константный char.

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

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

У меня есть опыт работы напрямую с libavcodec. Но пока не решился, не вижу смысла

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

затем буфер заполняет функция(вывод ffmpeg) и получается

А эта функция не куда не записывает количество записанных байт и кстати, как она называется?

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

То есть на вход strlen передается адрес буфера и дальше оно читает до конца...чего? Стека? Значит стек в моем случае 44 байта? Почему?

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

А блин. Я тебя не сразу понял. Я думал это ты пишешь, что это типа я раньше постил.

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

Нет. Первый ‘\0’ был на расстоянии 44 байта от начала буфера. Почему? Это неопределенное поведение. Может зависеть от многих факторов.

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

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

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

char const * const *p1 = nullptr, p2 = nullptr;

Это эквивалентно двум объявлениям:

// указатель на константный указатель на константный char
char const * const *p1 = nullptr;

// char, ему нельзя присваивать значение nullptr, поэтому ошибка компиляции
char p2 = nullptr;
rumgot ★★★★★
()
Ответ на: комментарий от gobot

strlen() ищет 0 пока не найдёт. это всё описание функции. ффмпег те нули которые ты записал в буфер переписал своими данными и strlen() находит 0 там где находит, где-то за пределами твоего буфера. ты можешь прочитать на один байт меньше чтобы ффмпег не переписывал последний 0.

вообще писать программы наугад плохо в любом языке. в языке для няшечек ты получишь какую-нибудь панику или исключение времени выполнения, в си (на котором ты сейчас пишешь), ты получишь неопределённое поведение.

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

Если функция чтения возвращает или записывает через указатель в один из параметров количество прочитанных байт, то почему бы этим не воспользоваться? Зафига еще раз по буферу пробегать?

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