Jump to content
Sign in to follow this  
Bernardo Vieira

Chamar callback por argumento de funcao

Recommended Posts

Bernardo Vieira

Bom, o meu objetivo é fazer um codigo que chame uma callback a partir de um argumento de uma funcao, vou deixar o meu codigo abaixo. A minha ideia é chamar o teste1, ou teste2, ou teste3 e poder quebrar a chamada (timer) a qualquer momento. Nesse codigo esta tudo a funcionar correto, cria o timer, entretanto eu qubro o timer e depois ele responde que nao, ou seja ja nao esta ativo, só que eu queria que, quando estiver ativo ele chama-se o timer1 ou timer2, ou timer3. Isso seria indicado num argumento do settimer

Alguem me sabe ajudar, Já nao é a primeira vez que tento fazer um codigo deste genero, mas sempre acabo por arrumar porque acho que nao tenho experiencia suficiente, mas desta vez gostaria mesmo de fazer, afinal de contas, esse codigo todo aí eu fiz em 10 minutos

Obrigado a todos desde já

para quem quiser ver o codigo com os tabs todos certos http://pastebin.com/iTZjR8PU

#include <iostream>
#include <unordered_map>
#include <chrono>
#include <thread>

struct timers {
bool ativo;
int milliseconds;
};
typedef std::unordered_map<int, struct timers*> map_time;
map_time Map_Time;
int t_time = 0;




void teste1() {
std::cout << "Ola" << std::endl;
return;
}
void teste2(int id, char letra, char frase[]) {
std::cout << id << " " << letra << " " << frase << std::endl;
return;
}
void teste3(std::string frase, char letra, int id) {
std::cout << frase.c_str() << " " << letra << " " << id << std::endl;
return;
}





void virtual_time(timers* timer) {
std::this_thread::sleep_for(std::chrono::milliseconds(timer->milliseconds));
if(timer->ativo) {
std::cout << "Sim" << std::endl;
}
else {
std::cout << "Nao" << std::endl;
}
return;
}
int settimer(int milliseconds) {
struct timers* timer = (struct timers*) malloc(sizeof(struct timers));
memset(timer, 0, sizeof(struct timers));
timer->ativo = true;
timer->milliseconds = milliseconds;
Map_Time.insert(map_time::value_type(t_time,timer));
std::thread v_timer(virtual_time,timer);
v_timer.detach();
return t_time;
}
bool killtimer(int timerid) {
map_time::iterator j = Map_Time.find(timerid);
if(j != Map_Time.end()) {
struct timers* timer = (struct timers*) malloc(sizeof(struct timers));
memset(timer, 0, sizeof(struct timers));
timer = j->second;
timer->ativo = false;
Map_Time.erase(timerid);
}
return true;
}

int main() {
std::cout << "Inicio" << std::endl;
int timer = settimer(5000);
std::this_thread::sleep_for(std::chrono::milliseconds(3000));
killtimer(timer);
std::this_thread::sleep_for(std::chrono::milliseconds(3000));

system("pause");
return 1;
}

Edited by Bernardo Vieira

Share this post


Link to post
Share on other sites
Flinger

Não sei c++, mas o teu código está muito C, por isso vou arriscar meter o bedelho.

Para declarares que uma função recebe uma callback, ou melhor, recebe um apontador para outra função, usas a sintaxe:


void funcaoprincipal(tipoRetorno (*funcao_a_chamar) (tipos_argumento))

um exemplo prático, vamos por a tua função killtimer a receber a função teste2 como argumento:

bool killtimer(int timerid, void (*funcao)(int,char,char*))

Para chamares a função killertime:

killtimer (a,&teste2);

Para chamares a calback dentro da função killtimer:

funcao(a,b,c);

No entanto, no teu caso existe um problema que por esta hora deves ter detectado. As tuas funções teste têm todas parâmetros diferentes...

Não sei se poderás contornar isso usando variable arguments, mas a função que invoca a callback teria de detectar qual é a callback a chamar, e passar os argumentos correctos, o que mata o sentido de usar callbacks.

Edited by Flinger
  • Vote 1

Share this post


Link to post
Share on other sites
pikax

ve se isto ajuda-te:

#include<stdio.h>
#include<functional>
#include<iostream>

typedef void (*myCall) (int);

void funccallback(int s)
{
   std::cout<<"std::function: o numero igual="<<s<<std::endl;
}

void ccallback(int s)
{
   std::cout<<"typedef C: o numero igual="<<s<<std::endl;
}


//C++
void ciclo(int b,int e, std::function<void(int)> fcall)
{
   for(int i=b;i<e;i++)
   {
    fcall(i);
   }
}


void cicloC(int b,int e, myCall fcall)
{
   for(int i=b;i<e;i++)
   {
    fcall(i);
   }
}

int main()
{
   //C++ func
   std::function<void(int)> func = funccallback;
   ciclo(0,10,func);
   ciclo(0,10,ccallback);

   ciclo(0,10,[=](int s)-> void {std::cout<<"LambdaCall o numero igual="<<s<<std::endl;});



   cicloC(0,10,ccallback);

   cicloC(0,10,[=](int s)-> void {std::cout<<"LambdaCall o numero igual="<<s<<std::endl;});

}

http://ideone.com/29T0fb

http://ideone.com/29T0fb

Edited by pikax
  • Vote 1

Por muito mais que que estude só aprendo uma coisa, que ainda tenho muita coisa para aprender.

A beleza de um código está em decompor problemas complexos em pequenos blocos simples.

"learn how to do it manually first, then use the wizzy tool to save time."

"Kill the baby, don't be afraid of starting all over again. Fail soon, learn fast."

Share this post


Link to post
Share on other sites
HappyHippyHippo

existe 2º razões para a minha solução :

- C++

- independência da assinatura do callback

class Timer {
 public:
   virtual ~Timer() {}
   virtual void callback(void) = 0;
}

class MyTimer : public Timer {
 public:
   MyTimer(/* os parâmetros que queiras */int param1, std::string param2) : _param1(param1), _param2(param2) { }
   virtual ~MyTimer() {}

  virtual void callback(void) {
     std::cout << "param1 = " << this->_param1 << std::endl;
     std::cout << "param2 = " << this->_param2 << std::endl;
  }

 protected:
   int _param1;
   std::string _param2;
}

Timer * my_timer = new MyTimer(2, "string");

// agora é só chamar a função de callback
my_timer->callback();

conclusão : só tens de gerir a instância MyTimer apontada pelo ponteiro my_timer

struct timers {
 bool ativo;
 int milliseconds;
 Timer * timer; // <--- onde vais guardar a instância da tua classe
};

int settimer(int milliseconds, Timer * timer) {
 // ...

 timer->ativo = true;
 timer->milliseconds = milliseconds;
 timer->timer = timer;

 // ...
}

void virtual_time(struct timers* timer) { // <--- falta o struct
 // ...

 if(timer->ativo) {
   timer->timer->callback(); // <---- chamada do callback
 }

 // ...
}

no entanto, como foi dito, esse código está muito C-style, e não verifiquei se isso corre mesmo como um timer

-------------------------------------------------------------------------------------------

exemplo completo e funcional em C++

#include <iostream>
#include <unordered_map>
#include <chrono>
#include <thread>
#include <cstring>

class Timer {
   public:
       Timer(int delta) : _thread(NULL), _active(true), _run(false), _delta(delta) {}
       virtual ~Timer() { this->stop(); }
       virtual void callback(void) = 0;

       bool isActive(void) { return this->_active; }
       bool isRunning(void) { return this->_run; }
       int  getDelta(void) { return this->_delta; }
       void setDelta(int delta) { this->_delta = delta; }

       void activate(void) { this->_active = true; }
       void deactivate(void) { this->_active = false; }

       void run(void) {
           if (this->_thread == NULL) {
               this->_run = true;
               this->_thread = new std::thread(Timer::proc, this);
           }
       }
       void stop(void) {
           this->_run = false;
           if (this->_thread != NULL) {
               this->_thread->join();
               this->_thread = NULL;
           }
       }

   protected:
       static void proc(Timer * timer) {
           bool running;
           do {
               std::this_thread::sleep_for(std::chrono::milliseconds(timer->getDelta()));

               running = timer->isRunning();
               if (running && timer->isActive()) {
                   timer->callback();
               }
           } while (running);
       }

   protected:
       std::thread * _thread;
       bool _active;
       bool _run;
       int  _delta;
};

class MyTimer : public Timer {
   public:
       MyTimer(int delta, int param1, std::string param2) : Timer(delta), _param1(param1), _param2(param2) { }
       virtual ~MyTimer() {}

      virtual void callback(void) {
         std::cout << "param1 = " << this->_param1 << std::endl;
         std::cout << "param2 = " << this->_param2 << std::endl;
      }

   protected:
       int _param1;
       std::string _param2;
};

int main() {
   std::cout << "Inicio" << std::endl;

   Timer * timer = new MyTimer(1000, 1, "string");
   timer->run();

   std::this_thread::sleep_for(std::chrono::milliseconds(10000));

   delete timer;

   std::cout << "Fim" << std::endl;

   return 0;
}

Edited by HappyHippyHippo
  • Vote 2

IRC : sim, é algo que ainda existe >> #p@p

Share this post


Link to post
Share on other sites
Bernardo Vieira

Desde já um grande obrigado aos 3 :)

mas responderam um bocadinho da forma que eu ja sabia fazer... na verdade eu quero é fazer um sistema que chame uma callback, seja lá qual for o nome dela e independetemente dos valores, tipo, podia ter som int, ou int, char, int ou char, char, int

enfim, sem uma regra exata. Mas bom, todos me ajudaram mais um pouco, e desculpem-me o facto de estar muito em estilo C, eu uso sempre o VSC++ e acabo por me esquecer desse pormenor.

@Flinger, dessa forma eu também sei fazer, mas obrigado

@pikax, interessante por acaso, acho que vou conseguir o que quero com uma pequena modificacao nisso. Obrigado

@HappyHippyHippo sim, ainda aprendi umas coisas :P só que eu queria independencia de parametros tambem ... Mas muito obrigado

Mais uma vez obrigado a todos :)

Edited by Bernardo Vieira

Share this post


Link to post
Share on other sites
HappyHippyHippo

na verdade eu quero é fazer um sistema que chame uma callback, seja lá qual for o nome dela e independetemente dos valores, tipo, podia ter som int, ou int, char, int ou char, char, int

enfim, sem uma regra exata.

e onde é que o meu exemplo invalida isso ? ao contrário das outras soluções que estás limitado a essa mesma permissa (isto sem o uso das variadic functions como o @Flinger referiu)


IRC : sim, é algo que ainda existe >> #p@p

Share this post


Link to post
Share on other sites
pikax
soluções que estás limitado a essa mesma permissa

nao tem muito nexo teres um timer que vai chamar uma callback com argumentos que nao conheces, a menos que passes para o timer quais sao os argumentos para essa funcao... mesmo assim seria uma ma' implementacao, se necessitas de chamar uma callback com N de argumentos deveras usar uma lambda!


Por muito mais que que estude só aprendo uma coisa, que ainda tenho muita coisa para aprender.

A beleza de um código está em decompor problemas complexos em pequenos blocos simples.

"learn how to do it manually first, then use the wizzy tool to save time."

"Kill the baby, don't be afraid of starting all over again. Fail soon, learn fast."

Share this post


Link to post
Share on other sites
HappyHippyHippo

(usando o código acima)

queres um callback use dois int's e uma string ?

class MyTimer2 : public Timer {
   public:
       MyTimer2(int delta, int param1, int param2, std::string param3)
          : Timer(delta), _param1(param1), _param2(param2), _param3(param3) { }
       virtual ~MyTimer2() {}

      virtual void callback(void) {
          // ... do stuff ...
      }

   protected:
       int _param1;
       int _param2;
       std::string _param3;
};

// ...
   Timer * timer = new MyTimer2(1000, 1, 2, "string");
   timer->run();

queres um callback use int int's e uma string e um vector de doubles ?

class MyTimer3 : public Timer {
   public:
       MyTimer3(int delta, int param1, std::string param2, std::vector<double> param3)
          : Timer(delta), _param1(param1), _param2(param2), _param3(param3) { }
       virtual ~MyTimer3() {}

      virtual void callback(void) {
          // ... do stuff ...
      }

   protected:
       int _param1;
       std::string _param2;
       std::vector<doubles> _param3;
};

// ...
   Timer * timer = new MyTimer3(1000, 1, "string", new std::vector<doubles>());
   timer->run();

como vês, os parâmetros são dados na criação do timer. podes usar o que quiseres ...


IRC : sim, é algo que ainda existe >> #p@p

Share this post


Link to post
Share on other sites
Bernardo Vieira

@pikax nao é propriamente desconhecido

@HappyHippyHippo ahh, entao eu tendi certo, pensei que tavas era a dizer outra coisa

Voce ainda nao entenderam, eu queria fazer algo do genero

MyTimer(nomecallback[], intervalo, fomatacao[], argumentos);

com os valores

MyTimer("teste2", 5000,"ics", 5, 'a', "ja funciona");

ou

MyTimer("teste3", 5000,"sci", "ja funciona", 'a', 5);

(teste2 e teste3 referem-se ao meu primeiro codigo)

ou seja, uma callback, um tempo e a seguir parametros, a minha duvida é, como é que eu chamo a callback, porque o par de parametros talvez eu consiga!

Share this post


Link to post
Share on other sites
pikax
como vês, os parâmetros são dados na criação do timer. podes usar o que quiseres ...
a menos que passes para o timer quais sao os argumentos para essa funcao

esse tipo de implementacao nao e' a melhor devido que o timer fica limitado devido ao facto que teres que criar uma class derivada do timer sempre que queres um tipo de timer diferente...

class DelegateTimer : public Timer {
public:
	MyTimer(std::function<void(void)> func) _func(func){ }
	virtual ~MyTimer() {}

   virtual void callback(void) {
	  _func();
   }
protected:
  std::function<void(void)> _func;
};


void myFunction(std::string str, int start, int end)
{
 ///......etc...
}

//....
Timer * timer = new DelegateTimer([]->{ myFunction("Xenx",0,10);});
timer->run();


int i = 1234;
int b = 12344;
std::string ddd = "P@P";

//memory leak 

timer = new DelegateTimer([&]->
{
  for (int a = 0; a < 50; ++a)
  {
	std::cout<<ddd<<i<<b<<std::endl;
  }
} );
timer->run();

btw nem se deveria usar threads, usar promisses seria o mais correcto neste caso.

EDIT:

Utilizar lambdas para criar um wrapper numa funcao, e' a forma mais flexivel de criar uma callback, para mais ele em principio nao ira' querer o valor de retorno da callback ou ja' sabera' quais serao os argumentos que a funcao ira' ter.

Edited by pikax
  • Vote 1

Por muito mais que que estude só aprendo uma coisa, que ainda tenho muita coisa para aprender.

A beleza de um código está em decompor problemas complexos em pequenos blocos simples.

"learn how to do it manually first, then use the wizzy tool to save time."

"Kill the baby, don't be afraid of starting all over again. Fail soon, learn fast."

Share this post


Link to post
Share on other sites
Bernardo Vieira

wow .... bem, mais uma vez obrigado, era isso mesmo, vou usar como exemplo ^^ obrigado :)

Share this post


Link to post
Share on other sites
HappyHippyHippo

btw nem se deveria usar threads, usar promisses seria o mais correcto neste caso.

porque ? não existe necessidade de garantia de retorno de valor

nenhuma das funções de callback apresentada retorna uma valor sequer


IRC : sim, é algo que ainda existe >> #p@p

Share this post


Link to post
Share on other sites
pikax

porque ? não existe necessidade de garantia de retorno de valor

mas ha' a necessidade de garantia que seja exception-safe, alem do mais se fizeres spawns a +10 timers, iras ter muitas threads a gastar concorrerem entre si pelo CPU.

nenhuma das funções de callback apresentada retorna uma valor sequer

por isso mesmo e' que nao existe necessidade de criar uma class derivada sempre que queres uma callback com argumentos diferentes

Edited by pikax

Por muito mais que que estude só aprendo uma coisa, que ainda tenho muita coisa para aprender.

A beleza de um código está em decompor problemas complexos em pequenos blocos simples.

"learn how to do it manually first, then use the wizzy tool to save time."

"Kill the baby, don't be afraid of starting all over again. Fail soon, learn fast."

Share this post


Link to post
Share on other sites
HappyHippyHippo

mas ha' a necessidade de garantia que seja exception-safe

existe ?

alem do mais se fizeres spawns a +10 timers, iras ter muitas threads a gastar concorrerem entre si pelo CPU.

10 threads não é nada principalmente quando a função principal e um sleep


IRC : sim, é algo que ainda existe >> #p@p

Share this post


Link to post
Share on other sites
Bernardo Vieira

Bom, mais uma vez obrigado a todos, e com uma coisa aqui outra ali, sempre consegui fazer o que queria.

deiam uma olhada http://pastebin.com/ZCjAZuqa

a grande vantagem é que eu nao preciso dizer qual é a callback nem quais sao os argumentos, nada, ou seja, agora, vou criar uma pequena callback, e passar o std::bind por typename e já funcionará corretamente, ou acham melhor usar tipo #define ?

bom, eu lembrei-me do bind porque ele aparece no boost e como o C++11 tras muitas coisas do boost, acabei por me lembrar

Este codigo é uma versao de teste, mas funciona, já agora gostaria de saber a vossa opiniao, á algo que deva mudar para melhorar ?

Abraço para todos :)

Edited by Bernardo Vieira

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×
×
  • Create New...

Important Information

By using this site you accept our Terms of Use and Privacy Policy. We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.