2015-06-12 4 views
0

Я хочу вызвать следующую функцию и передать ей функцию с параметром. Целью этого является то, что он должен вызывать функцию с моим указанным параметром, чтобы я знал, что вызвало функцию (в этом случае булавку gpio на малиновой Pi).Передача функции обратного вызова с параметром функции

int wiringPiISR(int pin, int edgeType, void (*function)(void)); 

В настоящее время у меня есть:

for (int i = 0; i < myValues.size(); ++i) 
{ 
    int myValue = myValues[ i ]; 
    wiringPiISR(myValue, INT_EDGE_RISING, &myCallback(myValue)); 
} 

Хотя это дает мне следующую ошибку:

error: lvalue required as unary ‘&’ operand

который я не могу понять, как к моему пониманию, MyValue является именующим или нет?

Это то, что я хочу сделать даже возможно? Если да, то как? Функция wiringPiISR из библиотеки под названием wiringPi, и я хотел бы избежать ее модификации как можно больше.

+0

Вы имеете в виду функцию, которая принимает параметр, но имеет предопределенное значение для него, чтобы его можно было вызывать без аргументов? – imreal

+0

Не совсем уверен, что вы имеете в виду, но я так думаю. Все, что я хочу выполнить, - дать функции wiringPiISR функцию для вызова с предоставленным параметром, так что wiringPiISR вызывает функцию с предоставленным параметром. Думаю, я использовал неправильный термин здесь, отредактировал вопрос. – PTS

+1

'& myCallback (myValue))' такой же как '& (myCallback (myValue))), поэтому вы берете адрес на возвращаемое значение' myCallback'. поскольку 'myValue' уже является первым параметром, вам может потребоваться передать' & myCallback'. Но мы не можем сказать из текущего контекста. –

ответ

3

Вы можете комбинировать ответы от imreal и Ryan Haining примерно так.

std::function<void()> cbfunc; 

void myCallback() 
{ 
    cbfunc(); 
} 
void myWiringPiISR(int val, int mask, std::function<void()> callback) 
{ 
    cbfunc = callback; 
    wiringPiISR(val, mask, &myCallback); 
} 

... а затем использовать его ...

void myActualCallback(int v) 
{ 
    ... do something... 
} 

myWiringPiISR(myValue, INT_EDGE_RISING, std::bind(myActualCallback, myValue)); 

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

Как это работает? Просто «std :: bind» связывает функцию и ее параметры в один объект std: function, который затем может быть «вызван» из функции myCallback, которая действует как прокладка вокруг обратного вызова, который вы передаете. Я раньше задавал функцию обратного вызова, но это редактирование, надеюсь, исправлено.

+0

@RyanHaining - Спасибо.Глобалы действительно хромые, но я не хотел загромождать ответ дальше. – Roddy

+0

Не могли бы вы немного объяснить, что здесь происходит? Это немного выше моих знаний. Как обратный вызов получит аргумент myValue? Ваш myCallback() не принимает никаких аргументов. – PTS

+0

Спасибо, редактирование было очень полезно! Это звучит немного безумно, делая это таким образом, но я с радостью воспринимаю это как решение. – PTS

0

Если у вас есть C++ 11, я предлагаю использовать std::function - это немного чище.

Если нет, ваша подпись функции неверна. Вам нужна обратная связь с типом void(int), но ваша функция принимает void()

1

Вам необходимо использовать std::function и std::bind.

Изменение подписи функции

int wiringPiISR (int pin, int edgeType, std::function<void()> func); 

Внутри вы можете вызвать функцию обратного вызова, просто используя func()

И ваш вызов:

int myValue = 3; 
wiringPiISR(myValue, INT_EDGE_RISING, std::bind(myCallback, myValue)); 

Что это делает создать std::function объект (т.е. вызываемый), который обертывает вашу функцию и сохраняет желаемое значение в своем состоянии.

Это будет работать только с C++11 и новее.

+0

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

+0

Необходимо изменить его. – imreal

+0

Downvoter поясните пожалуйста. – imreal

2

Если myValue является то, что вы можете решить, во время компиляции, вы можете установить его статически и использовать промежуточную функцию, чтобы передать.

void myCallbackHelper() { 
    static constexpr int myValue = 3; 
    myCallback(myValue); 
} 

wiringPiISR(myValue, INT_EDGE_RISING, &myCallbackHelper); 

Если вам нужно определить myValue во время выполнения, вы можете по-прежнему выполнять это, но не очень безопасно.

int& getMyValue() { 
    static int myValue; 
    return myValue; 
} 

void setMyValue(int i) { 
    getMyValue() = i; 
} 

void myCallbackHelper() { 
    myCallback(getMyValue()); 
} 

Затем установите его и вызовите

setMyValue(3); 
wiringPiISR(myValue, INT_EDGE_RISING, &myCallbackHelper); 
+0

Не будет ли это то же самое, что создавать целую функцию для каждого возможного обратного вызова? – PTS

+0

, если ваш обратный вызов должен принимать аргументы, и вы * необходимо * использовать указатель на функцию 'void (*) (void)', тогда ваши варианты будут ограничены и слегка болезненны. –

+0

Мне были бы интересны эти слегка болезненные варианты , исправление библиотечной проводкиPiISR каждый раз, когда я обновляю, будет продолжаться мягкая боль, поэтому я скорее хочу эту боль раз. – PTS

1

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

Сказав, что, есть причина, большинство API-вызовов с функцией-указатель обратного вызова выглядеть вроде этого

void setCallback(void (*function)(void* data), void* userdata); 

Это позволяет людям бросить их struct {blabla} data; добавить некоторые UserData, и когда функция вызывается, она передается вдоль.

Так что, в основном, помимо взлома со статическими переменными, вы не можете передавать какие-либо аргументы.

3

Вы можете «рвать» функцию. Это не требует определяемой пользователем изменчивой глобальной переменной и является потокобезопасной, если только у вас нет компилятора, который поддерживает несколько потоков, но не исключает потоковые исключения, которые в принципе непригодны для использования.

myWiringPiISRWrapper(Value value, int edge, std::function<void()> func) { 
    try { 
     throw &func; 
    } catch(...) { 
     myWiringPiISR(value, edge, [] { 
      try { 
       throw; 
      } catch(std::function<void()>* func) { 
       (*func)(); 
      } 
     }); 
    } 
} 

Это отвратительно и медленно, но это полностью инкапсулируются, который я думаю, что это стоит вверх. Обратите внимание, что это работает только в том случае, если обратный вызов никогда не выполняется после возврата вызова myWiringPiISR. В этом случае вы можете, конечно, иметь обратный вызов с любым связанным состоянием.

+0

Что значит, я никогда не смогу выполнить обратный вызов после вызова myWiringPiISR? Фактическая функция обратного вызова - это то, что я снова использую в своем коде в других точках. Нитевидный звук отличный, хотя я должен быть честным. Я не понимаю, как это решение работает. Это идет значительно выше моей головы. – PTS

+1

@ProfessorSparkles: Это означает, что если 'myWiringPiISR' где-то хранит указатель функции, а затем вызывает его в какое-то случайное время в будущем, произойдет очень плохое. Совершенно легально, если вы * вызываете функцию обратного вызова, когда захотите. Это плохо, если 'myWiringPiISR' не выполняет обратный вызов сразу, но сохраняет его, чтобы он был вызван обратно в ответ на какую-то другую вещь, которая не является вызовом' myWiringPiISRWrapper'. – Puppy

+0

О, я вижу. К сожалению, это именно то, что делает проводкаPiISR в фоновом режиме. Он будет вызывать обратный вызов в совершенно случайной точке в будущем и будет продолжать делать это. Он обрабатывает ввод пользователя и вызывает обратный вызов каждый раз, когда есть пользовательский ввод. – PTS