LINUX.ORG.RU

С++ вопросик про ссылку на фукнцию

 ,


0

2

Продолжаю изучать с++, запутался в указателях, а в гугле не могу правильно составить запрос. Мне нужно определить в структуре ссылку на функцию onBuffer, чтобы потом созданный thread ее вызывал. Как простой int передать я понял, а вот как функцию? Пробовал уже и слева и справа * ставить и & b и так и сяк )


int onBuffer(const char* data){
   
}

typedef struct ThreadPar {
    int sdtOut;      
    int *onBuffer; //как определить?
} ThreadPar, *PThreadPar;


PThreadPar ThreadParameters;
ThreadParameters->sdtOut = sdtOutRd;
ThreadParameters->onData = onBuffer; //как передать?

★★★★

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

Это – не С++. typedef struct сишники пишут, чтобы не писать перед всеми типами структур ключевое слово struct (например, struct A a;). В С++ такой необходимости нет, соответственно, достаточно просто писать struct ThreadPar.

typedef struct ThreadPar {
    int sdtOut;      
    int *onBuffer; //как определить?
} ThreadPar, *PThreadPar;

Зачем вы объявляете PThreadPar отдельным типом – тоже неясно. Это не Паскаль, в этом нет необходимости. Писать просто ThreadPar* проще и понятнее.

Теперь по теме.

Кратко:

int (*onBuffer)(const char*)

Долго: весь тред на стековерфлоу.

Siborgium ★★★★★
()

Вместо

int (*onBuffer)(const char*)

лучше использовать специальный шаблон

std::function<int(const char*)> f;

Это потому, что std::function может принимать функционашьные сущности разных типов.

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

std::function удобнее и читабельнее ручного указания типа. Это как std::string или std::shared_ptr.

Тогда лучше шаблонами тип параметризовать

Тип функции?

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

Набросал минимальный пример: https://godbolt.org/z/4GjK4K

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

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

Код:


#include <iostream>
#include <type_traits>

template <typename F>
struct ThreadPar {
    F onBuffer;
};

template <typename F>
ThreadPar(F&& f) -> ThreadPar<std::decay_t<F>>;

auto pprint(auto f, auto c) {
    std::cout << "[" << f << "] " << c << '\n';
}

int onBuffer(const char* c) {
    pprint("free function", c);
    return 0;
}

struct OnBuffer {
    int operator() (const char* c) {
        pprint("operator ()", c);
        return 0;
    }
};

int main() {
    ThreadPar t1 { onBuffer };
    ThreadPar t2 { [](auto c){ pprint("lambda", c); return 0; }};
    ThreadPar t3 { OnBuffer{} };
    auto c = "hello!";
    t1.onBuffer(c);
    t2.onBuffer(c);
    t3.onBuffer(c);
}

Выхлоп:

Program returned: 0
[free function] hello!
[lambda] hello!
[operator ()] hello!
Siborgium ★★★★★
()
Ответ на: комментарий от Siborgium

Ну т.е. ты сделал без std::function, хотя можно с ним. Чем оно лучше?

Хотя бы чтобы не писать этой конструкции уже аргумент в пользу std::function:

template <typename F>
ThreadPar(F&& f) -> ThreadPar<std::decay_t<F>>;
rumgot ★★★★★
()
Последнее исправление: rumgot (всего исправлений: 1)
Ответ на: комментарий от rumgot

Вопрос не в том, чем оно лучше, а в том, чем std::function хуже:

https://github.com/isocpp/CppCoreGuidelines/blob/036324/CppCoreGuidelines.md#t49-where-possible-avoid-type-erasure

https://rules.sonarsource.com/cpp/tag/bad-practice/RSPEC-5213

И в целом про специфику std::function:

https://quuxplusone.github.io/blog/2019/03/27/design-space-for-std-function/

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

Вопрос не в том, чем оно лучше, а в том, чем std::function хуже

Это то же самое.

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

Хотя бы чтобы не писать этой конструкции

Это просто deduction guide, чтобы руками тип не писать.

Это то же самое.

Да? Попробуйте раскомментировать вторую строку в main и сравните ассемблер в простом примере. Советую все же прочитать информацию по приведенным мной выше ссылкам. Говорят, помогает.

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

В таком случае прошу прощения, не так вас понял.

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

Ну кстати по второй ссылке рекомендуют использовать шаблонный тип или std::function по выбору. С оговоркой про необходимость например хранения в контейнере объектов std::function оборачивающих элементы разных типов. Замечание про вероятное отсутствие встроенности весомое, но тут я бы сначала смотрел на сложность алгоритма, который будет вызывать такую функцию, и не делал преждевременной оптиизации.

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

Я не говорил, что std::function не нужна. Она нужна – для одного конкретного юзкейса. Если известно хоть что-то кроме требуемой сигнатуры и необходимости type erasure, применение std::function уже не имеет особого смысла. По третьей ссылке в тексте размещены ссылки на аналогичные, более эффективные специализированные контейнеры. Кстати, std::function еще и не дружит с -fnoexceptions. В общем случае, нет никаких оснований использовать std::function в API.

Siborgium ★★★★★
()

на фукнцию

А что это?

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

Отвечу за него: это правила вывода типа для шаблона ThreadPar. Т.е. в случае, когда аргументом конструктора является r-value, тип F будет выведен как низведенный F, т.е. если F - это const или ссылка, то итоговый тип будет без константности и ссылочности.

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

А как бы те делал хранение в контейнере (list/vector/map/set) разных функциональных сущностей (лямбда/указатели на функции/функторы) без std::function ?

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

Ты только что описал ровно юзкейс std::function. Хотя и здесь при наличии чуть большей конкретики можно задействовать более специализированные контейнеры с лучшим результатом.

Siborgium ★★★★★
()

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

struct ThreadPar {
	int stdOut;
	int (*onData)(const char*); 
};

void ff(){
	ThreadPar *lpar = new ThreadPar;
	lpar->stdOut = 0;
	lpar->onData = onBuffer; //как передать? - прямо присвоить имя существующей функции(хотя бы продекларированной)

	//вызов указателя на функцию(где-то потом)
	lpar->onData(nullptr);
}

указатель на функцию пишется просто: сначала пишется тип результата этой функции - int, затем - как будет вызываться данный именованный указатель в скобках (* имя_указателя), затем список параметров функции на которую он указывает (const char*). получается - int, (* name), (const char *), поскольку для такого указателя звездочку при вызове ставить необязательно, то в декларации она есть, в вызове по нему она не нужна.

то есть это похоже на декларацию самой функции, в которой имя заменили на (* имя_указателя), и получилась переменная хранящая адрес точки входа некоей функции, которую в указатель присвоят.

//безклассовый пример 
int fff(){
	int(* func_ptr)(const char *); //это мы описали переменную func_ptr
	func_ptr = onBuffer; //это присвоили ей адрес точки входа в реальную функцию
	return func_ptr("hello"); //вызвали функцию
}
alysnix ★★★
()
Ответ на: комментарий от Siborgium

Да нет. Это я не как контраргумент привел, а просто из интереса. Как бы ты делал? Ну т.е. я бы наверное пробовал как-то засунуть то что мне передали внутрь функтора.

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

Т.е. эта магия нужна, потому что в самом объявлении структуры нельзя указать механизм проверки и приведения для типа F?

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

Если я правльно понял твой вопрос - то правила вывода - это и есть такой механизм. Это не магия - это именно такая функция - правила вывода.

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

Понял. Спасибо.

А этот механизм где-то регламентирован в стандарте или он получился accidentally, как метапрограммирование на шаблонах?

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

Этот механизм называется «deduction guide», его добавили в С++17 вместе с CTAD по-моему. Компилятор может сам вывести гайд для класса если в классе есть подходящий конструктор.

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

У Джосаттисса хорошая книга по шаблонам есть.

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

А, я не так прочитал вопрос. Да, можно за 10 минут накидать обертку над наследованием, переложив грязную работу на компилятор. Грязно и уродливо, но очень просто.

#include <array>
#include <iostream>
#include <type_traits>

template <typename S, typename ... Args>
struct Callable { 
    virtual S operator () (Args&& ... args) = 0;
};

template <typename F, typename ... Args>
struct Erased : public Callable<std::invoke_result_t<F, Args&&...>, Args...> {
    using S = std::invoke_result_t<F, Args&&...>;
    F f;
    Erased(F&& f): f(std::forward<F>(f)) { }
    S operator () (Args&& ... args) override {
        return f(std::forward<Args>(args)...);
    }
};

auto pprint(auto f, auto&& ... a) {
    std::cout << '[' << f << "]: ";
    ((std::cout << a << ' '), ...);
    std::cout << '\n';
}

void free_print(const char* c) {
    pprint("free function", c);
}

struct Printer {
    void operator () (const char* c) {
        pprint("operator ()", c);
    }
};

int main() {
    using C = Callable<void, const char*>;
    // deduction guide нормально не прикрутить
    C* f1 = new Erased<std::decay_t<decltype(free_print)>, const char*>(free_print);
    C* f2 = new Erased<Printer, const char*>(Printer{});
    // с лямбдой чуть сложнее из-за уникальности типа
    auto f3_ = [=](auto c) { pprint("lambda", c); };
    C* f3 = new Erased<decltype(f3_), const char*>(std::move(f3_));
    std::array fs { f1, f2, f3 };
    for (auto& f : fs) {
        (*f)("arg");
    }
}
[free function]: arg 
[operator ()]: arg 
[lambda]: arg 

Можно сделать нормально, но знаний и умений потребуется куда больше. Впрочем, на С++20 эту портянку можно было бы урезать по меньшей мере на треть.

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

И к чему вся эта партянка, если можно в одну строчку написать

    std::array<std::function<void(const char*)>, 3> ffs { free_print, Printer{}, [=](auto c) { pprint("lambda", c); } };

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

Вопрос исходный прочитай.

) разных функциональных сущностей (лямбда/указатели на функции/функторы) без std::function ?

«партянка» – просто пример того, как можно сделать это на коленке. Ни в коем случае не руководство к действию.

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

Запоздалый edit – под портянкой в последнем предложении подразумевался исходник по ссылке, а не приведенный в посте.

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

Вопрос прочел. Если на коленке пишется template <typename F, typename ... Args>, то std::function доступен. И данная партянка лютый говнокод в стиле - а я еще и так могу.

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

std::function получше будет, соглашусь. Так как там не будет аллокаций. small buffer optimization одна из моих самых любимых оптимизаций.

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

лютый говнокод в стиле - а я еще и так могу

…О чем я и написал в первом же предложении.

Если на коленке пишется […], то и std::function доступен

Абстрактное мышление совсем атрофировалось?

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

Ответ по сути - напишу свой std::function.

Ну так да. Спасибо, кэп. Он тебе уже несколько раз повторил - это не претендует на идеальность. Это сильно черновой вариант.

rumgot ★★★★★
()

рекомендую таки обзавестись книгой о языке (тот же Страуструп) или хотя бы какой-то тьюториал пройти (по C++ их должно быть дофига в инете, и даже бесплатных). Иначе ты для любой новой темы по-прежнему не сможешь составить поисковый запрос, и в итоге на обучение уйдет больше времени. Тот же указатель на функцию - это база, и она гуглится с результатом по первой найденной страничке.

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