Вызов функции, соответствующей заданной строке

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

    Например, так ActionScript пытается вызвать функцию test с тремя аргументами str, false, 1.0(соответственно типы аргументов: String, Boolean, Number):
    <invoke name="test" returntype="xml"><arguments><string>str</string><false/><number>1.0</number></arguments></invoke>
    

    Хотелось бы, чтобы со стороны C++ была вызвана соответствующая функция:
    void test_handler(const std::wstring& str, bool flag, double n);
    


    Под катом — реализация с использованием нового стандарта и, для сравнения, реализация с использованием старого стандарта(и капельки boost-а).

    Приступим. Для начала нужно как-то разобрать строку. Поскольку это не суть задачи, то, для разбора xml, будем использовать Boost.PropertyTree. Всё это спрячем в спомогательный класс InvokeParser, который будет хранить имя функции и массив пар тип аргумента-его значение:
    InvokeParser
    #include "boost/property_tree/ptree.hpp"
    #include "boost/property_tree/xml_parser.hpp"
    
    namespace as3 {
    
    class InvokeParser
    {
    public:
    	using ArgsContainer = std::vector<
    		std::pair<
    			std::wstring, // Argument type
    			std::wstring // Argument value
    			>>;
    
    public:
    	InvokeParser()
    		: invoke_name_()
    		, arguments_()
    	{
    	}
    
    	bool parse(const std::wstring& str)
    	{
    		using namespace boost;
    		using namespace boost::property_tree;
    
    		try
    		{
    			std::wistringstream stream(str);
    			wptree xml;
    			read_xml(stream, xml);
    
    			// Are 'invoke' tag attributes and 'arguments' tag exists?
    			auto invoke_attribs = xml.get_child_optional(L"invoke.<xmlattr>");
    			auto arguments_xml = xml.get_child_optional(L"invoke.arguments");
    			if(!invoke_attribs || !arguments_xml)
    				return false;
    			// Is 'name' exists ?
    			auto name = invoke_attribs->get_optional<std::wstring>(L"name");
    			if(!name)
    				return false;
    			invoke_name_ = *name;
    
    			arguments_.reserve(arguments_xml->size());
    			for(const auto& arg_value_pair : *arguments_xml)
    			{
    				std::wstring arg_type = arg_value_pair.first;
    				std::wstring arg_value = arg_value_pair.second.get_value(L"");
    				if((arg_type == L"true") || (arg_type == L"false"))
    				{
    					arg_value = arg_type;
    					arg_type = L"bool";
    				}
    
    				arguments_.emplace_back(arg_type, arg_value);
    			}
    
    			return true;
    		}
    		catch(const boost::property_tree::xml_parser_error& /*parse_exc*/)
    		{
    		}
    		catch(...)
    		{
    		}
    		return false;
    	}
    
    	std::wstring function_name() const
    	{
    		return invoke_name_;
    	}
    
    	size_t arguments_count() const
    	{
    		return arguments_.size();
    	}
    
    	const ArgsContainer& arguments() const
    	{
    		return arguments_;
    	}
    
    private:
    	std::wstring invoke_name_;
    	ArgsContainer arguments_;
    };
    } // as3
    



    Теперь напишем шаблонный класс Type, который будет иметь один параметр — некий C++-тип, в который нужно будет превратить строку, а также узнать соответствующее имя со стороны ActionScript. Например:
    Type<short>::convert(L"20"); // Вернёт 20, тип short
    Type<short>::name(); // short для ActionScript это "number"
    

    Код шаблонного класса Type:
    template<typename CppType>
    struct Type :
    	std::enable_if<
    		!std::is_array<CppType>::value,
    		TypeHelper<
    			typename std::decay<CppType>::type>
    	>::type
    {
    };
    
    template<typename CppType>
    struct Type<CppType*>
    {
    };
    

    Здесь мы видим минимальные предосторожности для неосторожного пользователя данного шаблона, например, нельзя инстанцировать данный шаблон указателем на какой-то тип, так как мы не используем указатели(конкретно для ActionScript — указателей попросту нет). Конечно здесь не все предострожности, но их можно легко добавить.
    Как видно, основную роботу выполняет другой шаблонный класс TypeHelper. TypeHelper инстанцируется «голым» типом. Например, для const std::wstring& получится std::wstring и т.д. Это делается с помощью decay. И делается это для того, чтобы убрать различия между функциями который имеют сигнатуры типа f(const std::wstring&) и f(std::wstring). Делаем также некоторую предосторожность для массивов, так как в этом случае послушный decay прекрасно справится с роботой и мы получим не совсем то, что хотели.

    Основной шаблон TypeHelper. Он будет служить для преобразования чисел. В нашем случае он будет прекрасно работать для Арифметических типов, хотя, по-хорошему, нужно бы исключить все символьные типы(это сделать не сложно немножко модифицировав is_valid_type с помощью std::is_same).
    TypeHelper
    template<typename CppType>
    struct TypeHelper
    {
    private:
    	enum { is_valid_type = std::is_arithmetic<CppType>::value };
    public:
    	typedef typename std::enable_if<is_valid_type, CppType>::type Type;
    
    	static typename std::enable_if<is_valid_type, std::wstring>::type name()
    	{
    		return L"number";
    	}
    
    	// Convert AS3 number type from string to @CppType
    	static Type convert(const std::wstring& str)
    	{
    		double value = std::stod(str);
    		return static_cast<Type>(value);
    	}
    };
    


    Как видно, имя всех числовых типов, в случае ActionScriptnumber. Для преобразования со строки, сначала аккуратно преобразовываем в double, а потом в нужный нам тип.
    Также нам нужна другая обработка для типов bool, std::wstring и void:
    Полные специализации TypeHelper для bool, std::wstring, void
    template<>
    struct TypeHelper<bool>
    {
    	typedef bool Type;
    
    	static std::wstring name()
    	{
    		return L"bool";
    	}
    
    	static bool convert(const std::wstring& str)
    	{
    		return (str == L"true");
    	}
    };
    
    template<>
    struct TypeHelper<std::wstring>
    {
    	typedef std::wstring Type;
    
    	static std::wstring name()
    	{
    		return L"string";
    	}
    
    	static std::wstring convert(const std::wstring& str)
    	{
    		return str;
    	}
    };
    
    template<>
    struct TypeHelper<void>
    {
    	typedef void Type;
    
    	static std::wstring name()
    	{
    		return L"undefined";
    	}
    
    	static void convert(const std::wstring& /*str*/)
    	{
    	}
    };
    



    Вот и всё, по-сути! Осталось аккуратно соединить всё вместе. Поскольку нужно иметь удобный механизм создания обработчиков соответствующих функций(хранение всех обработчиков в контейнере и т.д.), создадим интерфейс IFunction:
    struct IFunction
    {
    	virtual bool call(const InvokeParser& parser) = 0;
    	
    	virtual ~IFunction()
    	{
    	}
    };
    

    А вот классы-наследники уже будут знать, какую функцию нужно вызвать для конкретного случая. Снова шаблон:
    template<typename ReturnType, typename... Args>
    struct Function :
    	public IFunction
    {
    	Function(const std::wstring& function_name, ReturnType (*f)(Args...))
    		: f_(f)
    		, name_(function_name)
    	{
    	}
    	
    	bool call(const InvokeParser& parser)
    	{
    		if(name_ != parser.function_name())
    			return false;
    		const auto ArgsCount = sizeof...(Args);
    		if(ArgsCount != parser.arguments_count())
    			return false;
    
    		auto indexes = typename generate_sequence<ArgsCount>::type();
    		auto args = parser.arguments();
    
    		if(!validate_types(args, indexes))
    			return false;
    
    		return call(args, indexes);
    	}
    
    private:
    	template<int... S>
    	bool validate_types(const InvokeParser::ArgsContainer& args, sequence<S...>)
    	{
    		std::array<std::wstring, sizeof...(Args)> cpp_types = { Type<Args>::name()... };
    		std::array<std::wstring, sizeof...(S)> as3_types = { args[S].first... };
    		return (cpp_types == as3_types);
    	}
    
    	template<int... S>
    	bool call(const InvokeParser::ArgsContainer& args, sequence<S...>)
    	{
    		f_(Type<Args>::convert(args[S].second)...);
    		return true;
    	}
    
    protected:
    	std::function<ReturnType (Args...)> f_;
    	std::wstring name_;
    };
    
    template<typename ReturnType, typename... Args>
    std::shared_ptr<IFunction> make_function(const std::wstring& as3_function_name, ReturnType (*f)(Args...))
    {
    	return std::make_shared<Function<ReturnType, Args...>>(as3_function_name, f);
    }
    


    Сначала покажу как использовать:
    void test_handler(const std::wstring& str, bool flag, double n)
    {
    	std::wcout << L"test: " << str << L", " << std::boolalpha << flag << ", " << n << std::endl;
    }
    
    int main()
    {
    	as3::InvokeParser parser;
    	std::wstring str = L"<invoke name=\"test\" returntype=\"xml\">"
    		L"<arguments><string>str</string><false/><number>1.0</number></arguments>"
    		L"</invoke>";
    	if(parser.parse(str))
    	{
    		auto function = as3::make_function(L"test", test_handler);
    		function->call(parser);
    	}
    }
    

    Перейдём к деталям. Во-первых, есть вспомогательная функция as3::make_function(), которая помогает не думать о типе передаваемого callback-а.
    Во-вторых, для того, чтобы пройтись(более правильно — распаковать «паттерн», смотрим ссылку Parameter pack(ниже)) по пакету параметров(как перевести? Parameter pack) используются вспомогательные структуры sequence и generate_sequence:
    template<size_t N, size_t... Sequence>
    struct generate_sequence :
    	generate_sequence<N - 1, N - 1, Sequence...>
    {
    };
    
    template<size_t...>
    struct sequence
    {
    };
    
    template<int... Sequence>
    struct generate_sequence<0, Sequence...>
    {
    	typedef sequence<Sequence...> type;
    };
    

    Подсмотрено на stackoverflow.
    В двух словах generate_sequence генерирует пакет параметров 0, 1, 2, ... N - 1, который сохраняется(читать: "выводится компилятором") с помощью sequence. Это всё нужно для Pack expansion(буду переводить как "распаковка пакета").
    Например:
    У нас есть пакет параметров typename... Args и функция f. Мы хотим вызвать f, передав каждое значение пакета некоторой функции, а результат этой функции уже передать f:
    template<typename... Args>
    struct Test
    {
    	template<int... S>
    	static bool test(const std::vector<pair<int, float>>& args, sequence<S...>)
    	{
    		f_(Type<Args>::convert(args[S].second)...);
    		return true;
    	}	
    };
    // где-то в коде test(args, typename generate_sequence<sizeof...(Args)>::type())
    

    Вызов test для Args = <int, float> превратится в вызов:
    f_(Type<int>::convert(args[0].second), Type<float>::convert(args[1].second))
    

    Вот и вся магия!
    Наша функция-член Function::validate_types создаёт два массива. Один массив содержит имена C++-типов в ActionScript-е, а другой - имена типов, со входящей строки. Если массивы не одинаковы - у нас некорректная сигнатура функции! И мы можем это диагностировать. Вот что значит мощь шаблонов!
    А вспомогательная функция-член call(const InvokeParser::ArgsContainer& args, sequence<S...>) делает то, что в примере выше, только для нашего случая.
    Всё - с помощью make_function() можно делать регистрацию обработчиков, поскольку мы имеем полиморфный тип IFunction, обработчики можно спокойно сохранять в массиве(да в чём угодно) и при поступлении очередной строки вызывать соответствующий обработчик.

    Итак, а теперь код для старого стандарта, для максимального количества аргументов равного четырём с использованием boost::function_traits и совсем немножко mpl :)

    InvokeParser
    class InvokeParser
    {
    public:
    	typedef std::vector<std::pair<std::wstring, std::wstring> > ArgsContainer;
    	typedef ArgsContainer::value_type TypeValuePair;
    
    public:
    	InvokeParser()
    		: invoke_name_()
    		, arguments_()
    	{
    	}
    
    	bool parse(const std::wstring& str)
    	{
    		using namespace boost;
    		using namespace boost::property_tree;
    
    		try
    		{
    			std::wistringstream stream(str);
    			wptree xml;
    			read_xml(stream, xml);
    
    			optional<wptree&> invoke_attribs = xml.get_child_optional(L"invoke.<xmlattr>");
    			optional<wptree&> arguments_xml = xml.get_child_optional(L"invoke.arguments");
    			if(!invoke_attribs || !arguments_xml)
    				return false;
    			optional<std::wstring> name = invoke_attribs->get_optional<std::wstring>(L"name");
    			if(!name)
    				return false;
    			invoke_name_ = *name;
    
    			arguments_.reserve(arguments_xml->size());
    			for(wptree::const_iterator arg_value_pair = arguments_xml->begin(), end = arguments_xml->end(); arg_value_pair != end; ++arg_value_pair)
    			{
    				std::wstring arg_type = arg_value_pair->first;
    				std::wstring arg_value = arg_value_pair->second.get_value(L"");
    				if((arg_type == L"true") || (arg_type == L"false"))
    				{
    					arg_value = arg_type;
    					arg_type = L"bool";
    				}
    
    				arguments_.push_back(TypeValuePair(arg_type, arg_value));
    			}
    
    			return true;
    		}
    		catch(const boost::property_tree::xml_parser_error& /*parse_exc*/)
    		{
    		}
    		catch(...)
    		{
    		}
    		return false;
    	}
    
    	std::wstring function_name() const
    	{
    		return invoke_name_;
    	}
    
    	size_t arguments_count() const
    	{
    		return arguments_.size();
    	}
    
    	const ArgsContainer& arguments() const
    	{
    		return arguments_;
    	}
    
    private:
    	std::wstring invoke_name_;
    	ArgsContainer arguments_;
    };
    


    TypeHelper
    template<typename CppType>
    struct TypeHelper
    {
    private:
    	// Arithmetic types are http://en.cppreference.com/w/cpp/language/types.
    	// Need to exclude 'Character types' from this list
    	// (For 'Boolean type' this template has full specialization)
    	typedef boost::mpl::and_<
    		boost::is_arithmetic<CppType>,
    		boost::mpl::not_<boost::is_same<CppType, char> >,
    		boost::mpl::not_<boost::is_same<CppType, wchar_t> >,
    		boost::mpl::not_<boost::is_same<CppType, unsigned char> >,
    		boost::mpl::not_<boost::is_same<CppType, signed char> > > ValidCppType;
    public:
    	// We can get C++ type name equivalent for AS3 "number" type only if
    	// C++ type @CppType is @ValidCppType(see above)
    	typedef typename boost::enable_if<
    		ValidCppType,
    		CppType>::type Type;
    
    	// Get AS3 type name for given @CppType(see @ValidCppType)
    	static
    	typename boost::enable_if<
    		ValidCppType,
    		std::wstring>::type name()
    	{
    		return L"number";
    	}
    
    	// Convert AS3 number type from string to @CppType(see @ValidCppType)
    	static
    	Type convert(const std::wstring& str)
    	{
    		double value = from_string<wchar_t, double>(str);
    		// TODO: Use boost type cast
    		return static_cast<Type>(value);
    	}
    };
    
    template<>
    struct TypeHelper<bool>
    {
    	typedef bool Type;
    
    	// AS3 type name for boolean type
    	static std::wstring name()
    	{
    		return L"bool";
    	}
    
    	// Convert AS3 boolean value from string to our bool
    	static bool convert(const std::wstring& str)
    	{
    		return (str == L"true");
    	}
    };
    
    template<>
    struct TypeHelper<std::wstring>
    {
    	typedef std::wstring Type;
    
    	static std::wstring name()
    	{
    		return L"string";
    	}
    
    	static std::wstring convert(const std::wstring& str)
    	{
    		// Ok, do nothing
    		return str;
    	}
    };
    
    template<>
    struct TypeHelper<void>
    {
    	typedef void Type;
    
    	// AS3 type name for void type..
    	static std::wstring name()
    	{
    		return L"undefined";
    	}
    
    	static void convert(const std::wstring& /*str*/)
    	{
    		// Oops..
    		ASSERT_MESSAGE(false, "Can't convert from sring to void");
    	}
    };
    
    // @TypeHelper provides implementation
    // only for "number" type(arithmetic, without characters type), bool, string and void.
    // For any other type @TypeHelper will be empty.
    // decay is used for removing cv-qualifier. But it's not what we want for arrays.
    // That's why using enable_if
    template<typename CppType>
    struct FlashType :
    	boost::enable_if<
    		boost::mpl::not_<
    			boost::is_array<CppType> >,
    		TypeHelper<
    			typename std::tr1::decay<CppType>::type>
    	>::type
    {
    };
    
    // Partial specialization for pointers
    // There is no conversion from AS3 type to C++ pointer..
    template<typename CppType>
    struct FlashType<CppType*>
    {
    	// static assert
    };
    



    То, чего не было ранее - FunctionCaller - заменяет распаковку....:
    FunctionCaller
    template<int N>
    struct FunctionCaller
    {
    	template<typename Function>
    	static bool call(Function /*f*/, const InvokeParser::ArgsContainer& /*args*/
    #if defined(DEBUG)
    		, const std::wstring& /*dbg_function_name*/
    #endif
    		)
    	{
    		ASSERT_MESSAGE_AND_RETURN_VALUE(
    			false,
    			"Provide full FunctionCaller specialization for given arguments count",
    			false);
    	}
    };
    
    template<>
    struct FunctionCaller<0>
    {
    	template<typename Function>
    	static bool call(Function f, const InvokeParser::ArgsContainer& /*args*/
    #if defined(DEBUG)
    		, const std::wstring& /*dbg_function_name*/
    #endif
    		)
    	{
    		// Call function without args
    		f();
    		return true;
    	}
    };
    
    template<>
    struct FunctionCaller<1>
    {
    	template<typename Function>
    	static bool call(Function f, const InvokeParser::ArgsContainer& args
    #if defined(DEBUG)
    		, const std::wstring& dbg_function_name
    #endif
    		)
    	{
    		typedef FlashType<typename boost::function_traits<Function>::arg1_type> Arg1;		
    		const InvokeParser::TypeValuePair& arg = args[0];
    		if(Arg1::name() != arg.first)
    		{
    #if defined(DEBUG)
    			::OutputDebugStringW(Sprintf<wchar_t>(
    				L"Function: \"%s\":\n"
    				L"%s -> %s\n",
    				dbg_function_name.c_str(),
    				Arg1::name().c_str(), arg.first.c_str()).c_str());
    #endif
    			ASSERT_MESSAGE_AND_RETURN_VALUE(false, "Type mismatch", false);
    		}
    		// Call function with 1 arg
    		f(Arg1::convert(arg.second));
    		return true;
    	}
    };
    
    template<>
    struct FunctionCaller<2>
    {
    	template<typename Function>
    	static bool call(Function f, const InvokeParser::ArgsContainer& args
    #if defined(DEBUG)
    		, const std::wstring& dbg_function_name
    #endif
    		)
    	{
    		typedef FlashType<typename boost::function_traits<Function>::arg1_type> Arg1;
    		typedef FlashType<typename boost::function_traits<Function>::arg2_type> Arg2;
    		const InvokeParser::TypeValuePair& arg1 = args[0];
    		const InvokeParser::TypeValuePair& arg2 = args[1];
    
    		if((Arg1::name() != arg1.first) ||
    			(Arg2::name() != arg2.first))
    		{
    #if defined(DEBUG)
    			::OutputDebugStringW(Sprintf<wchar_t>(
    				L"Function: \"%s\":\n"
    				L"%s -> %s\n"
    				L"%s -> %s\n",
    				dbg_function_name.c_str(),
    				Arg1::name().c_str(), arg1.first.c_str(),
    				Arg2::name().c_str(), arg2.first.c_str()).c_str());
    #endif
    			ASSERT_MESSAGE_AND_RETURN_VALUE(false, "Type mismatch", false);
    		}
    		// Call function with 2 args
    		f(Arg1::convert(arg1.second),
    			Arg2::convert(arg2.second));
    		return true;
    	}
    };
    
    template<>
    struct FunctionCaller<3>
    {
    	template<typename Function>
    	static bool call(Function f, const InvokeParser::ArgsContainer& args
    #if defined(DEBUG)
    		, const std::wstring& dbg_function_name
    #endif
    		)
    	{
    		typedef FlashType<typename boost::function_traits<Function>::arg1_type> Arg1;
    		typedef FlashType<typename boost::function_traits<Function>::arg2_type> Arg2;
    		typedef FlashType<typename boost::function_traits<Function>::arg3_type> Arg3;
    		const InvokeParser::TypeValuePair& arg1 = args[0];
    		const InvokeParser::TypeValuePair& arg2 = args[1];
    		const InvokeParser::TypeValuePair& arg3 = args[2];
    
    		if((Arg1::name() != arg1.first) ||
    			(Arg2::name() != arg2.first) ||
    			(Arg3::name() != arg3.first))
    		{
    #if defined(DEBUG)
    			::OutputDebugStringW(Sprintf<wchar_t>(
    				L"Function: \"%s\":\n"
    				L"%s -> %s\n"
    				L"%s -> %s\n"
    				L"%s -> %s\n",
    				dbg_function_name.c_str(),
    				Arg1::name().c_str(), arg1.first.c_str(),
    				Arg2::name().c_str(), arg2.first.c_str(),
    				Arg3::name().c_str(), arg3.first.c_str()).c_str());
    #endif
    			ASSERT_MESSAGE_AND_RETURN_VALUE(false, "Type mismatch", false);
    		}
    		// Call function with 3 args
    		f(Arg1::convert(arg1.second),
    			Arg2::convert(arg2.second),
    			Arg3::convert(arg3.second));
    		return true;
    	}
    };
    
    template<>
    struct FunctionCaller<4>
    {
    	template<typename Function>
    	static bool call(Function f, const InvokeParser::ArgsContainer& args
    #if defined(DEBUG)
    		, const std::wstring& dbg_function_name
    #endif
    		)
    	{
    		typedef FlashType<typename boost::function_traits<Function>::arg1_type> Arg1;
    		typedef FlashType<typename boost::function_traits<Function>::arg2_type> Arg2;
    		typedef FlashType<typename boost::function_traits<Function>::arg3_type> Arg3;
    		typedef FlashType<typename boost::function_traits<Function>::arg4_type> Arg4;
    
    		const InvokeParser::TypeValuePair& arg1 = args[0];
    		const InvokeParser::TypeValuePair& arg2 = args[1];
    		const InvokeParser::TypeValuePair& arg3 = args[2];
    		const InvokeParser::TypeValuePair& arg4 = args[3];
    
    		if((Arg1::name() != arg1.first) ||
    			(Arg2::name() != arg2.first) ||
    			(Arg3::name() != arg3.first) ||
    			(Arg4::name() != arg4.first))
    		{
    #if defined(DEBUG)
    			::OutputDebugStringW(Sprintf<wchar_t>(
    				L"Function: \"%s\":\n"
    				L"%s -> %s\n"
    				L"%s -> %s\n"
    				L"%s -> %s\n"
    				L"%s -> %s\n",
    				dbg_function_name.c_str(),
    				Arg1::name().c_str(), arg1.first.c_str(),
    				Arg2::name().c_str(), arg2.first.c_str(),
    				Arg3::name().c_str(), arg3.first.c_str(),
    				Arg4::name().c_str(), arg4.first.c_str()).c_str());
    #endif
    			ASSERT_MESSAGE_AND_RETURN_VALUE(false, "Type mismatch", false);
    		}
    		// Call function with 4 args
    		f(Arg1::convert(arg1.second),
    			Arg2::convert(arg2.second),
    			Arg3::convert(arg3.second),
    			Arg4::convert(arg4.second));
    		return true;
    	}
    };
    



    И сам Function:
    Function
    struct IFunction
    {
    	virtual bool call(const InvokeParser& parser) = 0;
    
    	virtual ~IFunction()
    	{
    	}
    };
    
    template<typename FunctionPointer>
    struct Function :
    	public IFunction
    {
    	Function(const std::wstring& function_name, FunctionPointer f)
    		: f_(f)
    		, name_(function_name)
    	{
    	}
    	
    	bool call(const InvokeParser& parser)
    	{
    		typedef typename boost::remove_pointer<FunctionPointer>::type FunctionType;
    		enum { ArgsCount = boost::function_traits<FunctionType>::arity };
    
    		ASSERT_MESSAGE_AND_RETURN_VALUE(
    			name_ == parser.function_name(),
    			"Incorrect function name",
    			false);
    
    		ASSERT_MESSAGE_AND_RETURN_VALUE(
    			ArgsCount == parser.arguments_count(),
    			"Incorrect function arguments count",
    			false);
    
    		return FunctionCaller<ArgsCount>::template call<FunctionType>(
    			f_, parser.arguments()
    #if defined(DEBUG)
    			, name_
    #endif
    			);
    	}
    
    protected:
    	FunctionPointer f_;
    	std::wstring name_;
    };
    
    template<typename FunctionPointer>
    IFunction* CreateFunction(const std::wstring& name, FunctionPointer f)
    {
    	return new Function<FunctionPointer>(name, f);
    }
    



    Спасибо за внимание!

    UPD: забыл добавить - в самом новом, грядущем, 14м стандарте есть(будет) std::make_index_sequence, который делает всё то же, что и generate_sequence.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 12

      0
      Ок, вы изобрели свой SOAP
      Не верю что нет для этой цели стандартной библиотеки, yfghbvth axis.apache.org/axis/
        +3
        Если речь идет просто о вызове произвольных функций, то для этого есть libFFI, притом портируемая и с минимальными зависимостями.
          0
          Спасибо за наводку, буду знать.
        • UFO just landed and posted this here
            –1
            а потом другой программист подумал что это опечатка и убрал лишний $ и все сломалось. лучше уж использовать call_user_func
            +3
            Ожидал увидеть свою реализацию Qtшных сигналов и слотов :)
              –3
              Код тырили и на форумах расспрашивали?
              Видно по коду очень хорошо, учитывая, как намешан boost и stl из C++11. Если парсинг xml сделать например на pugixml, то boost можно выкинуть полностью, то что вы используете из boost, имеет либо аналоги в stl, либо реализуется совсем просто тем же stl. Но больше всего конечно убило полное непонимание variadic templates и логики работы и места назначения generate_sequence. FunctionCaller реализуется вариадиками без напрягов и не требует специализаций по кол-ву аргументов.

              Уж простите, но сначала стоит выучить C++11 (если уж используете его), потом учить других.

              У вас в итоге получилась каша из C++03, C++11, boost.
                +7
                Амм… Простите, что?

                Я же написал: сделаем сначала новым стандартом, а потом тоже самое — старым.
                Видно по коду очень хорошо, учитывая, как намешан boost и stl из C++11

                Покажите с++11 часть, где используется boost, кроме конечно, InvokeParser — и об этом я специально упомянул — парсинг строки не важен, по сути:
                Если парсинг xml сделать например на pugixml,

                Ага, заменить одну библиотеку — другой.
                Следующий пункт:
                Но больше всего конечно убило полное непонимание variadic templates и логики работы и места назначения generate_sequence.

                Покажите, как правильно, пожалуйста.
                И ещё:
                FunctionCaller реализуется вариадиками без напрягов и не требует специализаций по кол-ву аргументов.

                Старый стандарт, покажите, как вы будете вариадики в старом стандарте использовать и сразу же скажите об этом комитету по стандартизации — они, ведь, обманули — вариадики-же были и до нового стандарта, не правда ли?
                И ещё:
                boost можно выкинуть полностью, то что вы используете из boost, имеет либо аналоги в stl, либо реализуется совсем просто тем же stl

                Вы предлагаете свой boost::function_traits написать? Зачем? Это не сложно(файлик boost\trunk\boost\type_traits\function_traits.hpp содержит около 175 строчек несложных примеров использования частичной специализации шаблона) и отвлекает от решения задачи.

                У вас в итоге получилась каша из C++03, C++11, boost.

                Мешал только C++03 и boost(не учитываем InvokeParser), а не {C++03, boost, C++11} и {C++03, C++11}, и {boost, C++11}.

                Уж простите, но сначала стоит выучить C++11 (если уж используете его), потом учить других.

                Уж простите, сначала вы научите всех, как внимательно читать статью, а потом расскажите, как использовать вариадики в старом стандарте, а потом посоветуете для парсинга маленькой xml заменить одну библиотеку на другую.
                0
                -
                  0
                  Если я правильно понял пример, то было бы целесообразно для обозначениия типов использовать не строки а числа. Очевидно что это добавит лишний маппинг в InvokeParser, не то что бы это накладно по производительности, но это означает что нужно делать для каждого типа два маппинга, один и InvokeParser, второй в TypeHelper. Я дума это не так стращно как сравнивать строки для каждого вызова функции.
                    +1
                    По сути, сравнивать можно только при DEBUG, так как несоответствие типов происходит только по вине программиста(т.е, задал неправильный callback) — если производительность настолько важна. Но это немножко запутанно, так как придётся оговариваться — «этот тип обозначаем через 0, а этот через 1 и в TypeHelper::name() возвращаем для всех числовых типов 0, потому что смотри выше… и т.д.». Да, Вы абсолютно правы и если производительность совсем критична, и сравнивать типы на соответствие нужно при каждом вызове(кстати, можно сравнивать только первый раз, так как типы с++-функции не поменяются в рантайме… хм..), а не только при определённом DEBUG, то мапить типы можно и на char(unsigned char, если 128 типов о_О не хватает). В любом случае — это вопрос оптимизации, которая, к сожалению, в большинстве случаев приводит к менее читаемому коду…
                      +1
                      char(unsigned char, если 128 типов о_О не хватает) — не обращайте внимания, глупость:)

                  Only users with full accounts can post comments. Log in, please.