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&)> 

★★★★★

Вопрос 1: можно ли как-то обойтись без лямбды?

Отвечает Александр Друзь: ВСЕГДА можно обойтись без лямбды.

Pavval ★★★★★
()

перед вами образец читаемости шаблонов с++.

господ найдет и покарает всех, кто будет настаивить на том, что этот синтаксис лучший.

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

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

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

Шаблоны — это, по сути, отдельный язык со своим синтаксисом, совершенно не похожий на остальной С++. Ничего хорошего я в этом не вижу

Gvidon ★★★★
()

А чем тебе тут лямбда не угодила?

Ну можно std::bind пользовать (или свой аналог накидать, если уж велосипедить). Но не один ли фик в данном случае?

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

Я имею в виду как можно было бы, например, std::bind использовать вместо лямбды? С известным набором аргументов понятно как, а с переменным?

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

Ну можно std::bind пользовать

Вот поэтому и вопрос.

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

господ найдет и покарает всех, кто будет настаивить на том, что этот синтаксис лучший.

В расте, кстати, почти всегда страшнее будет. Плюс там вариадиков вообще нет. Насчёт D хз. А с чем ещё сравнивать?

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

перед вами образец читаемости шаблонов с++.

Можешь привести пример того же самого кода, но с таким синтаксисом, который был бы более читаем?

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

ну вот я бы предпочел увидеть чтонибудь такое.

class Base:
	fn bind_this(ref some) -> fn
		return some
class A <Base>:
	fn foo() -> int
		pass
	fn get_foo() -> fn
		return bind_this(ref foo)

Если нет читаемого варианта - то это не значит что я должен любить нечитаемый синтаксис

anonymous
()

Я вот смотрю на это, смотрю на открытый рядом gvim с ядерным кодом, и думаю что я правильно решил не становиться плюсовым программистом.

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

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

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

Ассемблер тоже тьюринг-полный язык, но тема не о нем.

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

господ найдет и покарает всех, кто будет настаивить на том, что этот синтаксис лучший.

Каких господ? А вообще, звучит почти как стих.

Господ найдёт
И покарает всех
Настаивать не смей,
Что этот синтаксис хорош!

korvin_ ★★★★★
()
Ответ на: комментарий от asaw
struct lambda_t
{
    int &a;
    int &b;
    double c;

    foo operator()(bar, zar &) const
};

lambda_t lambda = { a, b, c };

Это ровно тоже самое, что

auto lambda = [&a,&b,c](bar, zar&) -> foo {};

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

Нужно только добавить private. Нет никакого легально способа вытащить из лямбды то, что она там поназахватывала.

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

Это норм еще, за исключением void (T::*f)(Args...). Нет ни одного typename/template-костыля.

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

Можешь привести пример того же самого кода, но с таким синтаксисом, который был бы более читаем?

class Base:
	fn bind_this(ref some) -> fn
		return some
class A <Base>:
	fn foo() -> int
		pass
	fn get_foo() -> fn
		return bind_this(ref foo)

Можешь привести пример того же самого кода

того же самого кода

ahaha oh wow

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

ну вот я бы предпочел увидеть чтонибудь такое.

Это динамическая типизация или вывод типов? Если первое, то не эквивалент. Если второе, то в bind_this можно послать все, что угодно. Да и название метода неверное, this тут ни при чем. Ну и далее примерно то же самое.

Если нет читаемого варианта - то это не значит что я должен любить нечитаемый синтаксис

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

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

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

asaw ★★★★★
() автор топика
template <class This, class T, class... Args>
struct BaseClosure {
	This* _this;
	void (T::*_f)(Args...);
	
	auto operator() (Args&&... args)
		-> decltype((static_cast<T&>(*_this).*_f)(std::forward<Args>(args)...)) {
		return      (static_cast<T&>(*_this).*_f)(std::forward<Args>(args)...); 
	}
};

class Base {
protected:
	template <class T, class... Args>
	std::function<void(Args...)> bind_this_(void (T::*f)(Args...))
	{
		return BaseClosure<Base, T, Args...> {this, f};
	}
};

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); }
};

Второй вопрос не понял.

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

Отлично! Хотя мне интересно как избавиться конкретно вот от этого места:

auto operator() (Args&&... args)
То есть как получить args типа Args&&... без помощи всяких посторонних функций.

А второй вопрос как сделать фабрику таких функторов, чтобы можно было инстанцировать её экземпляр для конкретной пары объект-метод и доставать из неё функторы с помощью operator()().

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

void (T::*f)(Args...) Это че за штука? Тип какой-то функции? Почему тоже через std::function не записать, а то я не понимаю, что это значит?

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

То есть как получить args типа Args&&... без помощи всяких посторонних функций.

Опять не понял ._.

Args&&... - это не тип. Если вопрос про то, можно ли обойтись без variadic template-ов, то ответ нельзя (в общем случае).

А второй вопрос как сделать фабрику таких функторов, чтобы можно было инстанцировать её экземпляр для конкретной пары объект-метод и доставать из неё функторы с помощью operator()().

#include <functional>
#include <string>

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

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

int main() {
	A a;
	
	X<void, A, int, int&, std::string&> foo_functor_producer {&a, &A::foo};
	
	auto ff = foo_functor_producer();
	
	int t = 3;
	std::string s = "s";
	ff(3, t, s);

//	ff(3, 3, s); // <- не компилируется, значит ссылки работают.
	
	return 0;
}

Не пойму, зачем это нужно.

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

Не знал, что тип можешь требовать, чтобы функция была в каком-то классе. В первый раз о такой штуке слышу.

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

Опять не понял ._. Args&&... - это не тип.

Вот потому ты и не понял, что смотришь на слова, а не на суть. Что такое args?

X<void, A, int, int&, std::string&> foo_functor_producer {&a, &A::foo};
Не пойму, зачем это нужно.

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

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

Нет. Лол, кстати, у меня QtCreator крашится, если поставить курсор после ff (пытается понять какие аргументы у ff и дохнет).

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

кстати,

ff(3, 3, s); // <- не компилируется, значит ссылки работают.

означает, что Args&& с std::forward<Args> работают не так как надо...

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

Вот потому ты и не понял, что смотришь на слова, а не на суть. Что такое args?

Суть - это базворд, args - это parameter pack.

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

Просто оберни конструктор X в функцию. Я сделал как в стартовом посте. Завтра напишу, лень комп включать.

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

Суть - это базворд, args - это parameter pack.

So, how to say «parameter pack» in Russian? А главное - как его получить без костылей?

Просто оберни конструктор X в функцию.

Так а зачем? Конструктор меня полностью устраивает, меня параметры типа не устраивают, так сказать)

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

Да, ты прав, я привык, что везде константные, кроме этого примера)

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

So, how to say «parameter pack» in Russian?

Без понятния.

А главное - как его получить без костылей?

Каких костылей? Это божественная сущность введенная в c++11, у нее нет типа, и получить ее можно только конструкцией с тремя точками.

Так а зачем? Конструктор меня полностью устраивает, меня параметры типа не устраивают, так сказать)

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

#include <functional>
#include <string>

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

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

template <typename Ret, typename Class, typename... Args>
auto make_X(Class* _this, Ret (Class::*_f)(Args...)) -> X<Ret, Class, Args...> {
	return X<Ret, Class, Args...> {_this, _f};
}

int main() {
	A a;
	
	auto foo_functor_producer = make_X(&a, &A::foo);
	
	auto ff = foo_functor_producer();
	
	int t = 3;
	std::string s = "s";
	ff(3, t, s);
	
//	ff(3, 3, s); // <- не компилируется, ссылки работают.
	
	return 0;
}
Kuzy ★★★
()
Последнее исправление: Kuzy (всего исправлений: 2)
Ответ на: комментарий от Kuzy

Каких костылей? Это божественная сущность введенная в c++11, у нее нет типа, и получить ее можно только конструкцией с тремя точками.

Таких костылей, как лямбда (настоящая или самописная). Хочется чтобы можно было сделать что-то вроде:

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

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

Этот трюк не прокатит, потому что переменные-члены класса с типом auto объявлять нельзя, а именно для этого всё это нужно)

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

Таких костылей, как лямбда (настоящая или самописная). Хочется чтобы можно было сделать что-то вроде:

Откуда там по твоему возьмутся аргументы args?

Этот трюк не прокатит, потому что переменные-члены класса с типом auto объявлять нельзя, а именно для этого всё это нужно)

Нормально объясни что нужно. Причем тут переменные-члены класса еще понять бы.

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

Откуда там по твоему возьмутся аргументы args?

Оттуда же, откуда они и так берутся. Это те же самые Args и их даже можно обозвать там args, только видны эти args не будут.

Нормально объясни что нужно. Причем тут переменные-члены класса еще понять бы.

Я же объяснил: нужно чтобы foo_functor_producer можно было объявлять переменной-членом класса без необходимости заполнять руками все эти void, int, int&, std::string&.

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

Оттуда же, откуда они и так берутся. Это те же самые Args и их даже можно обозвать там args, только видны эти args не будут.

Это указатель на функцию он не содержит аргументов.

Я же объяснил: нужно чтобы foo_functor_producer можно было объявлять переменной-членом класса без необходимости заполнять руками все эти void, int, int&, std::string&.

#include <functional>
#include <string>

template <typename F>
struct X {};

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


template <typename Ret, typename Class, typename... Args>
auto make_X(Class* _this, Ret (Class::*_f)(Args...)) -> X<Ret(Class::*)(Args...)> {
	return X<Ret(Class::*)(Args...)> {_this, _f};
}

class A {
public:
	void foo(int arg1, int& arg2, std::string& str) {}
	
	X<decltype(&A::foo)> foo_functor_producer; // нет типов
//	X<decltype(&A::foo)> foo_functor_producer = make_X(this, &A::foo); // инициализация 
};

Можно макросов навернуть еще.

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

Это указатель на функцию он не содержит аргументов.

O RLY? А на что же ты тогда надеялся написав вот это вот:

X<decltype(&A::foo)>

?

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