Я строю программу на C ++, которая должна хранить карту строк для указателей на функции. Однако каждая функция может иметь разные типы и параметры возврата. Я пытаюсь решить эту проблему, создав функции, которые принимают массив пустых указателей и возвращают массив пустых указателей, а затем приводят аргументы и возвращаемые значения по мере необходимости.
Чтобы понять, как это будет работать, я пытаюсь создать простой манекен, но не могу его скомпилировать. Я пробовал несколько вещей, но продолжаю получать разные ошибки. вот пример:
#include <string>
#include <iostream>
#include <map>
using namespace std;
void** string2map(void** args){
//takes a string of the form "key:value;key:value;..." and returns a map<string,string>
string st = *((string**) args)[0];
map<string, string> result = map <string, string>();
//code doesnt matter
return (void*) &((void*) &result);
}
int main(){
string test = "hello:there;how:are you?";
map<string, string> result = *(map<string, string>**)string2map((void*) &((void*) &test))[0];
return 0;
}
когда я пытаюсь скомпилировать, я получаю:
void.cpp: In function 'void** string2map(void**)':
void.cpp:12:34: error: lvalue required as unary '&' operand
void.cpp: In function 'int main()':
void.cpp:17:89: error: lvalue required as unary '&' operand
Очевидно, здесь много чего не так, но я просто не знаю, с чего начать. Может кто-нибудь либо показать мне, что не так с кодом выше, или дать мне альтернативу тому, как я сейчас это делаю?
НОТА
Причина, по которой я возвращаю void**
вместо просто void*
в том, что может возникнуть ситуация, когда мне нужно вернуть несколько значений разных типов. Примером может быть, если выше, я хотел бы вернуть как полученную карту И количество записей в карте. Однако я даже не дошел до того, чтобы выяснить, как построить этот массив.
РЕДАКТИРОВАТЬ
Итак, исходя из полученных ответов, кажется довольно ясным, что это неправильный способ решения этой проблемы. Имея это в виду, кто-нибудь может предложить лучший вариант? Мне нужно иметь возможность хранить различные функции в одной карте, что означает, что мне нужно иметь возможность определять один тип данных для функций, которые принимают и возвращают различные типы. И важно иметь возможность возвращать несколько значений.
Вы конвертируете map<string,string>
к void**
, возвращая его затем преобразовав его обратно в map<string,string
, Почему бы просто не вернуть map<string,string>
? Это также называется string2map
что подразумевает, что вы когда-либо будете вызывать его только со строкой (подкрепленной тем фактом, что вы передаете строку, которая преобразуется в void**
затем переоборудован прямо назад). Если у вас нет веских причин для конвертации в и из void**
повсюду это, вероятно, то, что вам нужно:
#include <string>
#include <iostream>
#include <map>
using namespace std;
map<string, string> string2map(string st){
map<string, string> result = map <string, string>();
//code doesnt matter
return result;
}
int main(){
string test = "hello:there;how:are you?";
map<string, string> result = string2map(test);
return 0;
}
РЕДАКТИРОВАТЬ:
Я только что перечитал твой вопрос. Возможно, вы захотите посмотреть обобщенные функторы и посмотреть на Boost’s std::function
как возможные решения этой проблемы. Можно изменить тип возвращаемого значения функции через класс-оболочку, например:
template< class T >
class ReturnVoid
{
public:
ReturnVoid( T (*functor)() ) : m_functor( functor ) {}
void operator() { Result = functor(); }
private:
T (*m_functor)();
T Result;
};
// Specialise for void since you can't have a member of type 'void'
template<>
ReturnVoid< void >
{
public:
ReturnVoid( T (*functor)() ) : m_functor( functor ) {}
void operator() { functor(); }
private:
T (*m_functor)();
};
Использование этого в качестве оболочки может помочь вам сохранить функторы с разными типами возвращаемых значений в одном массиве.
Не обращая внимания на собственный ужас от идеи вопиющего отказа от безопасности типов, на ум сразу приходят две вещи.
Во-первых, что именно, на ваш взгляд, будет указано, когда string2map выйдет из области видимости?
Во-вторых, вам не нужно разыгрывать void *. Void * получает специальную обработку в C ++, поскольку к нему может быть применено все, что угодно.
Если вы настаиваете на том, чтобы попытаться это сделать, я бы начал с изменения типа возвращаемого значения на void, а затем принял бы void * в качестве входного параметра для вашей функции.
Например:
void string2map(void* args, void* returnedMap);
Таким образом, вы должны будете создать свою карту в области видимости, которая на самом деле иметь карта для указания.
$ 5.3.1 / 3 — «Результат одинарного & оператор является указателем на его
операнд. Операнд должен быть lvalue или квалифицированный.»$ 5.3.1 / 2 — «Результатом каждого из следующих унарных операторов является
prvalue.»
Таким образом, вы пытаетесь получить адрес значения, которое не разрешено.
Далее, C ++ не позволяет возвращать массив.
Итак, вы действительно хотите начать смотреть на то, что вы хотите. Возврат карты по значению вместо этого является одним определенным вариантом.
Я пытаюсь решить эту проблему, создав функции, которые принимают массив пустых указателей и возвращают массив пустых указателей, а затем приводят аргументы и возвращаемые значения по мере необходимости.
Это (действительно очень) плохо. Взгляните вместо этого на std :: function и std :: bind — они должны изящно покрывать различия между сигнатурами функций и связанными аргументами.
Причина, по которой я возвращаю void ** вместо просто void *, заключается в том, что может возникнуть ситуация, когда мне нужно будет вернуть несколько значений разных типов.
Затем верните объект, который содержит значения. Для обобщений взгляните на std :: tuple или boost :: any.
Вот некоторый код:
void function1(int, const char); // defined elsewhere
std::tuple<int,int> function2(std::string&); // defined elsewhere
std::map<std::string,std::function<void(void)>> functionmap;
functionmap.insert( std::make_pair("function1", std::bind(&function1, 2, 'c')) );
std::tuple<int,int> result;
functionmap.insert( std::make_pair("function2", [&result] {
result = function2("this is a test"); } );
// call function1
functionmap["function1"]();
// call function2
functionmap["function2"](); // result will now contain the result
// of calling function2
Это то, что вы пытались сделать?
int Foo(int a) { return a; }
typedef int (*FooFunc)(int);
void Bar(){}
typedef std::map<std::string, void*> FunctionMap;
// you should use boost::any or something similar instead of void* here
FunctionMap CreateFunctionMap(const std::string& args)
{
FunctionMap result;
result["Foo"] = &Foo;
result["Bar"] = &Bar;
return result;
}
void Call(FunctionMap::const_reference functionInfo)
{
// @hansmaad The key will give information on the signatures.
// there are a few distinct options, so it will be a conditional
// with a couple of clauses.
if (functionInfo.first == "Foo")
{
auto f = static_cast<FooFunc>(functionInfo.second);
std::cout << f(42);
}
else if (functionInfo.first == "Bar")
{
/* */
}
}
int main()
{
auto functions = CreateFunctionMap("...");
std::for_each(begin(functions), end(functions), Call);
}
@hansmaad Ключ даст информацию о подписи. Есть несколько различных вариантов, так что это будет условно с парой предложений. — Ewok 33 минут назад
В этом случае типичное решение выглядит так:
typedef void (*func_ptr)();
std::map<std::string, func_ptr> func_map;
map<string,string> string2map(string arg){
//takes a string of the form "key:value;key:value;..." and returns a map<string,string>
map<string, string> result = map <string, string>();
//...
return result;
}
// ...
// Add function to the map
func_map["map<string,string>(string)" = (func_ptr)string2map;
// Call function in the map
std::map<std::string, func_ptr>::iterator it = ...
if (it->first == "map<string,string>(string)")
{
map<string,string> (*func)(string) = (map<string,string>(*)(string))it->second;
map<string,string> result = func("key1;value1;key2;value2");
}
Для краткости я использовал приведение в С-стиле указателей на функции. Правильное приведение C ++ будет reinterpret_cast<>()
,
Указатели функций преобразуются в общий тип при вставке в карту и возвращаются к их правильному типу при вызове их.