1

What this code do:

Several commands executed to completion(if(current seconds == some seconds) stop). CommandProcessor(functor) run this commands. Pointer of this class I try to throw to pthread_create.

The question:

It is possible to use class object as callable?

command.h

#ifndef COMMAND_H
#define COMMAND_H

#include <iostream>
#include "commandprocessor.h"

class CommandProcessor;

using namespace std;

class Command
{
    static CommandProcessor *commandProcessor;
    time_t stopSeconds;

    public:
        Command(int _stopSeconds);
        static void setProcessor(CommandProcessor *_commandProcessor);
        void execute();
};

#endif // COMMAND_H

command.cpp

#include "command.h"

CommandProcessor* Command::commandProcessor = NULL;

Command::Command(int _stopSeconds)
{
    stopSeconds = _stopSeconds;
}

void Command::setProcessor(CommandProcessor *_commandProcessor)
{
    commandProcessor = _commandProcessor;
}

void Command::execute()
{
    time_t seconds;
    time_t now = time(NULL);
    seconds = localtime(&now)->tm_sec;
    cout << seconds << endl;
    if(seconds != stopSeconds)
        commandProcessor->addCommand(this);
}

commandprocessor.h

#ifndef COMMANDPROCESSOR_H
#define COMMANDPROCESSOR_H

#include <list>
#include "command.h"

using namespace std;

class Command;

class CommandProcessor
{
    list< Command* > commandsList;

    public:
        void addCommand(Command *_command);
        void operator()();
};

#endif // COMMANDPROCESSOR_H

commandprocessor.cpp

#include "commandprocessor.h"

void CommandProcessor::addCommand(Command *_command)
{
    commandsList.push_back(_command);
}

void CommandProcessor::operator()()
{
    while(commandsList.size() > 0)
    {
        Command *command = commandsList.front();
        commandsList.pop_front();
        command->execute();
    }
}

main.cpp

#include <iostream>
#include <pthread.h>
#include "commandprocessor.h"

#define MAX_THREADS 2

using namespace std;

int main(int argc, char **args)
{
    CommandProcessor *processor = new CommandProcessor();
    Command::setProcessor(processor);
    processor->addCommand(new Command(53));
    processor->addCommand(new Command(24));
    processor->addCommand(new Command(15));

    pthread_t threads[MAX_THREADS];
    for(int i=0; i < MAX_THREADS; i++)
    {
        pthread_create(&threads[i], NULL, processor(), NULL); // error: 'processor' cannot be used as a function
    }
    return 0;
}
3
  • 1
    pthread_create takes a void *(*start_routine) (void *). Pass the address of processor via the user-supplied argument and have the start_routine do a reinterpret_cast of the received object to the CommandProcessor type, and execute operator() on it. Also, why bother with the C thread API anyway? You should be using std::thread if possible. Commented Jul 22, 2014 at 18:56
  • 1
    prefer to use std::thread. Commented Jul 22, 2014 at 19:00
  • Please reduce your example to "CommandProcessor* processor;processor();", because that's what the error means that you get. Nothing to do with pthread_create. Concerning the question, the answer is "no, not directly", and both other commenters here have a good suggestion what to do instead. Commented Jul 22, 2014 at 19:33

1 Answer 1

4

DISCLAIMER : this is the kind of thing that you really shouldn't be doing in C++, as it includes messing around with void*. It needs to be used here because of the interaction with the C API. If you can, use std::thread from C++11 or boost::thread if you're using an older compiler.

To answer your question : yes, it is perfectly fine to make a class object as a callable (as a "function") once you implement operator() for it. You already did that, but the problem is the extra code needed to implement the interface between C++ and the C API for Pthreads.

The main reason why you're getting the "processor cannot be used as a function" error here is because processor is a CommandProcessor* and, as such, needs a dereference. However, even doing (*processor)() wouldn't help you, because pthread_create is defined as follows :

int pthread_create(phread_t *thread, const pthread_attr_t *attr,void *(*start_routine) (void *), void *arg);

This means that the function to be executed must return void* and take a void*. The documentation also states that the parameter of start_routine has the value of arg originally given to the respective call to pthread_create.

However, you cannot pass a pointer to CommandProcessor::operator()() to pthread_create for a much more important reason : it is a member function. All non-static class member functions need to be called with an object so that they can refer to this inside their body.

The solution to your problem is to make a static function that will match the prototype required by pthread_create, and pass the pointer to a CommandProcessor as the arg of pthread_create. Have a look at this :

static void* processor_executor(void *arg)
{
    CommandProcessor *processor = static_cast<CommandProcessor*>(arg);
    (*processor)();
    return NULL;
}

int main(int argc, char **argv)
{
    /* ... */
    for(int i=0; i < MAX_THREADS; i++)
    {
        pthread_create(&threads[i], NULL, processor_executor, static_cast<void*>        (processor));
    }
    /* ... */
}

This way, operator() is executed inside the thread created by pthread_create, using the same CommandProcessor object as the one in main().

Keep in mind that accessing the same data from within many parallel threads will lead to data races. Use mutexes, semaphores, or condition variables to protect the data from parallel access.

Sign up to request clarification or add additional context in comments.

2 Comments

The reinterpret_cast is used correctly here, with the conversions being the exact opposites. However, there is no need for a reinterpret_cast and it should even be avoided. Instead, just use a static_cast. You could even leave the static_cast to void* out, as that is the equivalent of the implicit conversion.
@UlrichEckhardt : thanks for the comment. My understanding of C++ casts is a bit rusty these days. I was under the (wrong) impression that casts to/from void* can only be made via reinterpret_cast.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.