Ir ao conteúdo
  • Cadastre-se

C++ Thread c++ não está aceitando membro de objeto como parâmetro por que?


sandrofabres

Posts recomendados

Bom , até que thread em c++ é até mais fácil do que em outras linguagens que eu conheço, mesmo assim, se eu fosse criar uma linguagem, simplesmente eu deixaria ainda mais fácil a criação de thread: dá mesma forma que temos static, public private e outras coisas para se mostrar o que a função é, eu inventaria os 'qualificadores' parallel e sequential, por exemplo:  parallel void Acelerador();     para função ficar rodando sem ter que terminar para o próximo código debaixo dela rodar. Enfim, é apenas uma ideia... Vai que um gênio que esteja vendo esse forum cria uma linguagem e se inspira nessa minha dica, né?

 

Mas voltando ao assunto, eu sei que não é usual ficar escrevendo o código da função(implementando) dentro da própria classe, mas eu vejo fazendo isso em vários códigos e não dá problema algum, eu acho mais prático e nunca deu erro, acontece que eu estou tentando passar um objeto e um membro dele para um thread, e ele dá o erro:

 

Motor::combustao': non-standard syntax; use '&' to create a pointer to member

 

Já refiz o código para usar ponteiro e e comercial e nada deu certo. O mais engraçado é que se eu não uso classe e objeto, o thread funciona de maneira tão fácil que eu até me sinto culpado. Sim gente, quando eu consigo escapar das burocracias de tipagem (char pra string e vice versa) dessa linguagem, eu me sinto culpado, como se tivesse cometido um crime, um pecado, sei lá...

Bom, segue o  código abaixo meu que deu o erro acima:


 

#include <iostream>
#include <thread>
using namespace std;

class Motor {
public:
    void combustao() {
        while(1){
            cout << "rodando" << endl;
        }
    }
};


int main()
{
    Motor MotorTurbo;
    thread ChaveIgnicao(MotorTurbo.combustao);
    ChaveIgnicao.join();
    return 0;
}

 

E o mais gozado, o IDE que eu uso não acha nenhum erro de sintaxe, o erro só aparece em tempo de compilação.

Alguém saberia fazer o MotorTurbo pegar combustão pegando thread através de POO?

 

 

 

 

 

Link para o comentário
Compartilhar em outros sites

@Flávio Pedroza  Obrigado, funcionou!

Eu nunca iria pensar em usar a classe de forma estática e mesmo assim ter que colocar o objeto gerado por essa classe ao lado dela novamente. Se alguém me mostrasse sem eu testar eu diria que seria uma hipérbole, só que não.

 

Obrigado.

Link para o comentário
Compartilhar em outros sites

Citação

Thread c++ não está aceitando membro de objeto como parâmetro por que?

 

Porque não é assim que funciona. 

 

Em 08/03/2020 às 18:30, Flávio Pedroza disse:

thread ChaveIgnicao(&Motor::combustao, MotorTurbo);

 

Acho que também não é assim...

 

Thread é uma classe, como os objetos são chamados em C++. A maneira mais simples de criar uma instância dessa classe, e essa é a nomenclatura em C++, seria declarar

    thread coisa;

e depois associar alguma função a essa coisa. Essa função se costuma chamar de CallBack, como em javascript por exemplo.E faz sentido: um thread pode ser associado durante a execução a várias possíveis funções, conforme a lógica.E essa possível função é a que o thread chama de volta quando ele inicia...

 

Uma outra maneira seria essa que você usou:

 

    thread coisa(Callback);

 

E assim associa diretamente uma função a um thread. Quando isso é conhecido em tempo de compilação faz sentido. E no seu caso simples é.

 

Só que CallBack pode ser uma de 3 coisas

  • um ponteiro para uma função
  • uma função lambda, aquelas do tipo []()
  • um functor 

Você passou

    thread ChaveIgnicao(MotorTurbo.combustao); // isso nao e um ponteiro para funcao

MotorTurbo.combustao que não é nenhum dos 3. Isso seria o endereço de uma função membro e só funcionaria se fosse declarada como static

 

@Flávio Pedroza escreveu 

    thread ChaveIgnicao(&Motor::combustao, MotorTurbo);

E esse também não é. Os argumentos seguintes a CallBack são passados como argumentos para a função a ser executada, e isso seria o caso ao passar uma instância da classe Motor para a função de Callback. Só que nesse exemplo essa função seria combustao() que não tem argumentos. 

 

Se você declarar a função fora da classe é claro que funciona, seria o caso 1. Se a CallBack aceitasse um parâmetro poderia usar 

    thread ChaveIgnicao( combustao, parametro );

 

 

Em 08/03/2020 às 16:44, sandrofabres disse:

o erro só aparece em tempo de compilação.

Alguém saberia fazer o MotorTurbo pegar combustão pegando thread através de POO?

 

Pode usar uma das 4 opções:

  1. usar uma função comum, não uma função membro
  2. deixar como está mas declarar a função ou a classe como estática
  3. usar combustao() como uma função lambda na declaração de ChaveIgnicao()
  4. escrever combustao como um functor

 

1 hora atrás, sandrofabres disse:

Obrigado, funcionou!

Eu nunca iria pensar em usar a classe de forma estática e mesmo assim ter que colocar o objeto gerado por essa classe ao lado dela novamente. Se alguém me mostrasse sem eu testar eu diria que seria uma hipérbole, só que não

 

Não entendo como pode ter funcionado. Poderia postar o código e dizem em que ambiente isso roda? Muito interessante de fato.

 

Amanhã se eu tiver tempo eu mostro as outras opções. e queria testar essa que funcionou!

 

Em 08/03/2020 às 16:44, sandrofabres disse:

até que thread em c++ é até mais fácil do que em outras linguagens que eu conheço, mesmo assim, se eu fosse criar uma linguagem, simplesmente eu deixaria ainda mais fácil a criação de thread: dá mesma forma que temos static, public private e outras coisas para se mostrar o que a função é, eu inventaria os 'qualificadores' parallel e sequential, por exemplo:  parallel void Acelerador();     para função ficar rodando sem ter que terminar para o próximo código debaixo dela rodar. Enfim, é apenas uma ideia... Vai que um gênio que esteja vendo esse forum cria uma linguagem e se inspira nessa minha dica, né

 

Quem sabe alguém inventa isso...

 

Ou pode usar

#include <execution>

em seu programa e testar em C++ mesmo. Veja alguma discussão em 

https://en.cppreference.com/w/cpp/header/execution ou 

https://devblogs.microsoft.com/cppblog/using-c17-parallel-algorithms-for-better-performance/

 

Use um compilador moderno.

 

Se tem mesmo interesse em algoritmos paralelos há decadas quem desenvolve isso é Intel e você pode baixar uma versão do compilador para FORTRAN ou C++ grátis por 30 dias. Ou ter uma licença grátis se você for estudante ou professor.

 

Pode aprender muito com isso em https://software.intel.com/en-us/parallel-studio-xe/choose-download

 

Ou pode usar a linguagem Go que tem esse paradigma de execução em paralelo e é grátis e bem moderna.E grátis em https://golang.org/

 

@sandrofabres Não me leve a mal por repetir isso, mas esse é um (outro) tópico complexo que te sugiro tratar com humildade. Você disse que era simples --- "mais simples que em outras linguagens que eu conheço" --- mas não usou mais que um thread, nenhum mecanismo de sincronismo, de proteção, join, detach, semáforos, mutexes, nada. E o teste de 20 linhas sequer funcionou e você disse que era mais simples que em outras linguagens.

 

 

Link para o comentário
Compartilhar em outros sites

:)

Voltando aos threads, vou deixar aqui um exemplo besta escrito na hora do almoço copiando do código original do autor do tópico.

 

É algo inútil, mas mostra a mecânica disso usando 3 threads, cada um com uma opção, e deve servir para quem procura um exemplo da mecânica dessas coisas.

 

Repito: esse é um tema MUITO complicado --- ou eu é que não aprendi. Esse tipo de aplicação tem o costume de dar erros infernais. São processos rodando ao mesmo tempo e em geral eles precisam compartilhar coisas, isso quer dizer tempo e memória. Então corrupção de memória e problemas de sincronismo são uma praga nesse campo.

 

Por outro lado, tem a chance de acelerar bem o programa e melhorar os tempos de resposta de tudo.

 

Thead 1: uma funçao comum

  • A função apenas mostra uma mensagem na tela, mostra uns "E " na tela para lembrar que é a função externa a classe, espera um tempo e encerra
void simples_externa(int valor)
{
    std::chrono::milliseconds um_tempo(500);
    cout << "funcao externa rodando."
        << endl;
    for (int i = 0; i < 20; i += 1)
    {
        cout << "E ";
        std::this_thread::sleep_for(um_tempo);
    }
    cout << endl;
    cout << "funcao externa vai dar um tempo por... " <<
        valor << "ms" << endl;
    std::chrono::milliseconds tempo(valor);
    std::this_thread::sleep_for(tempo);
    cout << "funcao externa de volta."
        << endl;
    cout << "funcao externa encerrando."
        << endl;
};  // simples_externa

Thead 2: um functor --- closure? função objeto? --- não sei como chama isso em português

  • Nesse caso vai rodar a função combustao() do autor na classe Motor.

Como vai rodar, já que eu tinha dito que não era possível?
 

Derivando MotorDiesel de Motor criamos a variável valor e um functor que chama combustao() passando o valor. Só como exemplo. O valor vai ser o tempo da pausa do thread em milissegundos. E a fila anda. A fila dos threads


Eis o código

class MotorDiesel : Motor
{
public:
    MotorDiesel() { valor = 2000; };
public:
    void operator()() { combustao(valor); };
}; //   class MotorDielsel

Alterei o código de combustao() copiando claro o código da outra função: mostra mensagem, mostra uns "F " para lembrar que usa a opção de functor, faz uma pausa e encerra

 

Eis a combustao() alterada

    void combustao(int valor)
    {
        std::chrono::milliseconds um_tempo(500);
        cout << "***** MotorDiesel: functor rodando."
            << endl;
        for (int i = 0; i < 20; i += 1)
        {
            cout << "F ";
            std::this_thread::sleep_for(um_tempo);
        }
        cout << endl;
        cout << "functor vai dar um tempo por... " <<
            valor << "ms" << endl;
        std::chrono::milliseconds tempo(valor);
        std::this_thread::sleep_for(tempo);
        cout << "functor de volta."
            << endl;
        cout << "functor: encerrando."
            << endl;
    };  // combustao()

Thread 3: uma função lambda

  • Declarei uma função em main copiada claro das outras duas e chamada MotorWankel que faz a mesma coisa: se identifica, mostra uns "L" --- para indicar que é a opção lambda --- na tela e faz uma pausa. Ao encerrar a pfunção termina

Eis o código:

    auto motor_wankel = [&valor]()
    {
        std::chrono::milliseconds um_tempo(500);
        cout << "***** MotorWankel: lambda rodando."
            << endl;
        for (int i = 0; i < 20; i += 1)
        {
            cout << "L ";
            std::this_thread::sleep_for(um_tempo);
        }
        cout << endl;
        cout << "lambda vai dar um tempo por... " <<
            valor << "ms" << endl;
        std::chrono::milliseconds tempo(valor);
        std::this_thread::sleep_for(tempo);
        cout << "lambda de volta."
            << endl;
        cout << "lambda: encerrando."
            << endl;
    };  // motor_wankel[]

Como testar isso?

 

Simples: declara os 3 threads e roda. A tela claro vai ficar um pouco zoada porque main() e os threads escrevem ao mesmo tempo. Depois de iniciar os caras main() usa join() para esperar pelo final deles e encerra o programa quando os 3 terminarem.

 

Eis o código de main()

int main(int argc, char** argv)
{
    Motor turbo;
    MotorDiesel diesel;
    int     valor = 1500;

    auto motor_wankel = [&valor]()
    {
        std::chrono::milliseconds um_tempo(500);
        cout << "***** MotorWankel: lambda rodando."
            << endl;
        for (int i = 0; i < 20; i += 1)
        {
            cout << "L ";
            std::this_thread::sleep_for(um_tempo);
        }
        cout << endl;
        cout << "lambda vai dar um tempo por... " <<
            valor << "ms" << endl;
        std::chrono::milliseconds tempo(valor);
        std::this_thread::sleep_for(tempo);
        cout << "lambda de volta."
            << endl;
        cout << "lambda: encerrando."
            << endl;
    };  // motor_wankel[]

    thread Chave1(simples_externa, 3000);
    thread Chave2((MotorDiesel()));
    thread Chave3(motor_wankel);

    cout << "main(): thread disparado para rodar a funcao lambda" << endl;
    cout << "main(): thread disparado para rodar a funcao externa" << endl;
    cout << "main(): thread disparado para rodar functor em MotorDiesel" << endl;
    cout << "***** th funcao externa id = " << Chave1.get_id() << endl;
    cout << "***** th functor id = " << Chave2.get_id() << endl;
    cout << "***** th lambda id = " << Chave3.get_id() << endl;
    cout << "main(): esperando pelo thread("
        << Chave1.get_id()
        << ")" << endl;
    cout << "main(): esperando tambem pelo thread("
        << Chave2.get_id()
        << ")" << endl;
    cout << "main(): esperando tambem pelo thread("
        << Chave3.get_id()
        << ")" << endl;
    Chave1.join();
    Chave2.join();
    Chave3.join();
    cout << "main(): rodando de novo. Acabaram os 3"
         << endl;
    return 0;
};  // main()


A saída do programa

 

A cada vez que roda a saída muda um pouco :) mas vai ser algo assim

main(): thread disparado para rodar a funcao lambda
main(): thread disparado para rodar a funcao externa
main(): thread disparado para rodar functor em MotorDiesel
***** th funcao externa id = 5912
***** th functor id = 6800
***** th lambda id = 11668
main(): esperando pelo thread(5912funcao externa rodando.
E ***** MotorDiesel: functor rodando.
F ***** MotorWankel: lambda rodando.)
main(): esperando tambem pelo thread(6800
L )
main(): esperando tambem pelo thread(11668)
E F L E F L E F L E F L E F L E F L E F L E F L E F L E F L E F L E F L E F L E F L E F L E F L E F L E F L E F L
funcao externa vai dar um tempo por... 3000ms

functor vai dar um tempo por... 2000ms

lambda vai dar um tempo por... 1500ms
lambda de volta.
lambda: encerrando.
functor de volta.
functor: encerrando.
funcao externa de volta.
funcao externa encerrando.
main(): rodando de novo. Acabaram os 3

Os E F e L são a saída das 3 funções rodando ao mesmo tempo que main() como esperado...

 

Eis o programa todo. Roda em qualquer plataforma, mas aqui só uso Windows 10. 

#include <iostream>
#include <thread>

using namespace std;

void    simples_externa(int);

class Motor
{
public:
    int valor;
public:
    void combustao(int valor)
    {
        std::chrono::milliseconds um_tempo(500);
        cout << "***** MotorDiesel: functor rodando."
            << endl;
        for (int i = 0; i < 20; i += 1)
        {
            cout << "F ";
            std::this_thread::sleep_for(um_tempo);
        }
        cout << endl;
        cout << "functor vai dar um tempo por... " <<
            valor << "ms" << endl;
        std::chrono::milliseconds tempo(valor);
        std::this_thread::sleep_for(tempo);
        cout << "functor de volta."
            << endl;
        cout << "functor: encerrando."
            << endl;
    };  // combustao()
}; //   class Motor


class MotorDiesel : Motor
{
public:
    MotorDiesel() { valor = 2000; };
public:
    void operator()() { combustao(valor); };
}; //   class MotorDiesel

int main(int argc, char** argv)
{
    Motor turbo;
    MotorDiesel diesel;
    int     valor = 1500;

    auto motor_wankel = [&valor]()
    {
        std::chrono::milliseconds um_tempo(500);
        cout << "***** MotorWankel: lambda rodando."
            << endl;
        for (int i = 0; i < 20; i += 1)
        {
            cout << "L ";
            std::this_thread::sleep_for(um_tempo);
        }
        cout << endl;
        cout << "lambda vai dar um tempo por... " <<
            valor << "ms" << endl;
        std::chrono::milliseconds tempo(valor);
        std::this_thread::sleep_for(tempo);
        cout << "lambda de volta."
            << endl;
        cout << "lambda: encerrando."
            << endl;
    };  // motor_wankel[]

    thread Chave1(simples_externa, 3000);
    thread Chave2((MotorDiesel()));
    thread Chave3(motor_wankel);

    cout << "main(): thread disparado para rodar a funcao lambda" << endl;
    cout << "main(): thread disparado para rodar a funcao externa" << endl;
    cout << "main(): thread disparado para rodar functor em MotorDiesel" << endl;
    cout << "***** th funcao externa id = " << Chave1.get_id() << endl;
    cout << "***** th functor id = " << Chave2.get_id() << endl;
    cout << "***** th lambda id = " << Chave3.get_id() << endl;
    cout << "main(): esperando pelo thread("
        << Chave1.get_id()
        << ")" << endl;
    cout << "main(): esperando tambem pelo thread("
        << Chave2.get_id()
        << ")" << endl;
    cout << "main(): esperando tambem pelo thread("
        << Chave3.get_id()
        << ")" << endl;
    Chave1.join();
    Chave2.join();
    Chave3.join();
    cout << "main(): rodando de novo. Acabaram os 3"
         << endl;
    return 0;
};  // main()


void simples_externa(int valor)
{
    std::chrono::milliseconds um_tempo(500);
    cout << "funcao externa rodando."
        << endl;
    for (int i = 0; i < 20; i += 1)
    {
        cout << "E ";
        std::this_thread::sleep_for(um_tempo);
    }
    cout << endl;
    cout << "funcao externa vai dar um tempo por... " <<
        valor << "ms" << endl;
    std::chrono::milliseconds tempo(valor);
    std::this_thread::sleep_for(tempo);
    cout << "funcao externa de volta."
        << endl;
    cout << "funcao externa encerrando."
        << endl;
};  // simples_externa

 

Link para o comentário
Compartilhar em outros sites

Em 08/03/2020 às 16:44, sandrofabres disse:

mas eu vejo fazendo isso em vários códigos e não dá problema algum, eu acho mais prático

 

Na verdade em geral é o contrário: nada prático.

  • Um caso é o mundo dos estudantes e dos curiosos, onde seu programa é pequenino e tem poucas classes ou muitas vezes nenhuma, como se vê muito nestes foruns por exemplo. A vida desses programas acaba quando o trabalho é entregue, quando a dúvida é sanada, coisas assim. E o programa depois de pronto não roda nunca mais. Muitos nunca são executados fora do IDE, por exemplo. Sempre são recompilados e criados a cada execução.
     
  • Um outro caso é o uso real de programas. Na prática: a vida de um programa começa quando ele fica pronto. E nunca mais é compilado. Quem usa nunca viu ou sabe o que é o código.
  • E para escrever esses programas muitas vezes você usa dezenas de classes que não foi você que escreveu. E usa classes que você escreveu anos atrás, mas não vai ficar compilando ou incluindo a cada programa novo que você faz. Então cada classe é uma unidade de compilação em si, e NA PRÀTICA você não quer misturar com seu programa atual porque não precisa compilar de novo e só vai atrapalhar. Imagina compilar printf() a cada vez que usa, por exemplo. Onde está a fonte?

E porque DOIS arquivos para cada classe?

 

A razão é a mesma: quando você compila a sua super classe de lista ligada por exemplo, aquela que você usa toda hora em seus programas, você gera uma biblioteca e isso você guarda para seu uso, ou vende, ou copia para outros micros. Para usar você só precisa desse arquivo, um coisas.lib ou coisas.a ou coisas.dll ou coisas.so, que vai conter seu código ou o código de alguém. Mas você só compila quando muda algo, quando acha um erro ou cria uma versão melhor.

 

Mas como compilar um programa que usa essas coisas e não tem sequer o código fonte?  Usando os headers, os tais coisa.h.

 

Essa é a razão. É mais prático, não menos.

Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...