LINUX.ORG.RU

Шаблонов магия

 


3

2

Вопрос отсюда. Есть такой вот код, который работает:

class Base {
protected:
    template <class T, class... Args>
    std::function<void(Args...)> bind_this_(void (T::*f)(Args...))
    {
        return [this, f](Args&&... args) { (static_cast<T&>(*this).*f)(std::forward<Args>(args)...); };
    }
};

class A : public Base {
    void foo(int arg1, int& arg2, std::string& str) {}
public:
    std::function<void (int, int&, std::string&)> get_foo_functor() { return bind_this_(&A::foo); }
};

A::get_foo_functor() привязывает к A::foo() указатель на экземпляр A (который неявный this) и возвращает обертку, которую можно использовать как функциональный объект с аргументами, такими же как у A::foo().

Вопрос 1: можно ли как-то обойтись без лямбды? Вопрос 2: как сделать шаблонный класс X с оператором operator()(), который, будучи инстанцироваьным с нужными параметрами, возвращал бы такой же функтор. Типа вот такого:

A a;
X< ... > foo_functor_producer(/* например */ &a, &A::foo);
auto ff = foo_functor_producer(); //< возвращает std::function<void (int, int&, std::string&)> 

★★★★★

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

Я передаю тип функции A::foo и потом из него вытаскиваю тип класса, типы аргументов и тип возвращаемого значения.

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

args это всего лишь имя для function parameter pack, как мы выяснили выше.

args - да, args... - нет.

void (T::*f)(Args&&... args)

Вот здесь args... не имеет смысла, лень проверять, скомпилируется это или нет.

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

Скомпилируется конечно, это ничему не противоречит. Кстати, ещё раз повторю: там args - имя для Args...

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

Действительно.

void foo(void (*)(int a, int b)) { 
//	return a + b; // use of undeclared identifier
}

уау

such c++

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

ну да, выглядит натянуто если писать наподобие с++. потому как в нормальных языках не должно быть магии по получения указателя на метод. что то наподобие: A a = A() fn v= ref a.foo io.wrap(v)

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

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

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

anonymous
()

Никак не пойму, почему же это полные по Тьюрингу шаблоны вызывают столько вопросов у цепепешников? :-) Может быть с ними что-то не так? :-)

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

Речь шла про синтаксис.

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

typename some_metafunc<pama1, typename another_metafunc<param>::type>::type
Костыли вокруг того, чтоб норм распарсить < >, которые еще могут быть операторами.

Или вот это:

a*b;
Это объявление переменной типа a или умножение двух переменных (может быть переопределенный operator * с сайд-эффектами внутри)?

Или вообще ПУШКА - это синтаксис конструкторов: конструктор объявляется как имя структуры... но... в c++ есть анонимные структуры... ии... У НИХ НЕЛЬЗЯ ОБЪЯВИТЬ КОНСТРУКТОР.

А самая ПУШКА, в том, что, на самом деле, у них конструктор есть:

struct {
  int a = 3;
  float b = 1.f;
} t;
Ура, у безымянной структуры есть недефолтный, неявный конструтор который инициализирует a и b.

Можешь троллить этим крестовиков.

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

Я упростил решение, пользуясь тем, что указатели на функции известны компилятору и могут быть параметрами шаблонов:

#include <functional>
#include <string>

template <typename F, F>
struct X {};

template <typename Ret, typename Class, typename... Args, Ret(Class::*F)(Args...)>
struct X<Ret(Class::*)(Args...), F> {
    Class* _this;
    Ret (Class::*_f)(Args...);
    X(Class* c) : _this(c), _f(F) {}

    std::function<Ret(Args&&...)> operator() () {
        return [&] (Args&&... args) { (_this->*_f)(std::forward<Args>(args)...); };
    }
};

class A {
public:
    A() : foo_functor_producer(this) {} // никаких make_X() не нужно
    void foo(int arg1, int& arg2, std::string& str) {}

    X<decltype(&A::foo), &A::foo> foo_functor_producer;
};

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

Можно ещё причесать и избавиться от _f.

Вот как-то так:

template <typename F, F>
struct X {};

template <typename Ret, typename Class, typename... Args, Ret(Class::*F)(Args...)>
struct X<Ret(Class::*)(Args...), F> {
    Class* _this;
    X(Class* c) : _this(c){}

    std::function<Ret(Args&&...)> operator() () {
        return [&] (Args&&... args) { (_this->*F)(std::forward<Args>(args)...); };
    }
};

class A {
public:
    A() : foo_functor_producer(this) {}
    void foo(int arg1, int& arg2, std::string& str) {}

    X<decltype(&A::foo), &A::foo> foo_functor_producer; // нет типов
};

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

Ура!

Вообще, можно вместо make_X писать так:

X<decltype(&A::foo)> foo_functor_producer {this, &A::foo};
Я make_X, просто, из прошлого примера скопипастил.

Но втащить указатель в темплейтный параметр хорошая идея - меньше оверхед в рантайме.

Кстати, тут _f уже не нужен, можно сразу F использовать.

#include <functional>
#include <string>

template <typename F, F>
struct X {};

template <typename Ret, typename Class, typename... Args, Ret(Class::*F)(Args...)>
struct X<Ret(Class::*)(Args...), F> {
	Class* _this;
	
	std::function<Ret(Args&&...)> operator() () {
		return [&] (Args&&... args) { (_this->*F)(std::forward<Args>(args)...); };
	}
};

class A {
public:
	void foo(int arg1, int& arg2, std::string& str) {}
	
	X<decltype(&A::foo), &A::foo> foo_functor_producer {this};
};
Kuzy ★★★
()
Ответ на: комментарий от asaw

Похоже, что нет: http://rextester.com/PWYDZ14254 Тупо ICE, как обычно это и бывает с VC++.

Error(s):
source_file.cpp(29) : fatal error C1001: An internal error has occurred in the compiler.
(compiler file 'f:\dd\vctools\compiler\cxxfe\sl\p1\c\template.cpp', line 22679)
 To work around this problem, try simplifying or changing the program near the locations listed above.
Please choose the Technical Support command on the Visual C++ 
 Help menu, or open the Technical Support help file for more information
        source_file.cpp(29) : see reference to class template instantiation 'X<void (__thiscall A::* )(int,const int &,const std::string &),A::foo>' being compiled

GCC 4.9 понимает, ну а древний clang 3.5 у них там с известным багом, который не позволял std::function присваивать лямбду.

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

Точнее баг (регрессия) был не в clang, а в стандартной библиотеке libc++: https://llvm.org/bugs/show_bug.cgi?id=17798 С гнутой libstdc++ всё и старым шлангом должно как надо собираться.

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