I want to create a C++ application in Visual Studio 2010 which contains 2 threads:
read data from a extern file
write data to an extern file
I already read the theory about threading but don't really understand how I can use it. Is there anybody who can show me how I can simply define and run these 2 threads in Visual Studio 2010?
Currently I have the following example which doesn't work:
class Thread
{
public:
Thread();
int Start(void * arg);
protected:
int Run(void * arg);
static void * EntryPoint(void*);
virtual void Setup();
virtual void Execute(void*);
void * Arg() const {return Arg_;}
void Arg(void* a){Arg_ = a;}
private:
THREADID ThreadId_;
void * Arg_;
};
Thread::Thread() {}
int Thread::Start(void * arg)
{
Arg(arg); // store user data
int code = thread_create(Thread::EntryPoint, this, & ThreadId_);
return code;
}
int Thread::Run(void * arg)
{
Setup();
Execute( arg );
}
/*static */
void * Thread::EntryPoint(void * pthis)
{
Thread * pt = (Thread*)pthis;
pt->Run( pt->Arg() );
}
virtual void Thread::Setup()
{
// Do any setup here
}
virtual void Thread::Execute(void* arg)
{
// Your code goes here
}
I am also open for good tutorials or code examples.
If the program does not compile, you should include the headers of the undefined functions as specified in http://www.MSDN.com . Also make shure the thread is not started from a DLL entrypoint (such as DllMain()).
Also, you should compile with multithreading enabled (i believe it is /MT option).
Related
So I have the following c++ class which stores a std::thread as a member variable, and starts a member function in that separate thread. However, this code only builds in visual studio code (using msys2 12.1.0 as a G++ compiler), and causes errors when I try to build it in visual studio code.
The line that causes the error seems to be: foo_thread = std::thread(threadFunction, this);
In visual studio code I get a red underline warning saying "no instance of constructor std::thread::thread" matches the argument list", however the code still compiles and runs fine.
In visual studio I get the same error, and a warning "C3867 'Foo::threadFunction': non-standard syntax; use '&' to create a pointer to member", and the code does not compile. When I try foo_thread = std::thread(&threadFunction, this); the error goes away, however when I try to build I get the error "&: illegal operation on bound member function expression. foo_thread = std::thread(&threadFunction, this); does still compile and run on visual studio code however.
How do I make this section work as desired and able to compile across c++ compilers/ides? It is also worth mentioning I don't seem to get these errors when the function that is handed to the newly spawned thread is not a member function, however I need it to be a member function for this program.
Code:
#include <thread>
#include <windows.h>
#include <iostream>
#define DllExport __declspec( dllexport )
typedef void (*funcPtr)(int num);
class Foo {
public:
// Value Constructor
Foo(int a, int b, funcPtr f) {
foo = a;
bar = b;
myFunc = f;
}
int getFoo() { return foo; };
int addBar(int a) { return private_method(a); };
void callMyFunc(int n) { myFunc(n); };
// Start Monitor
void StartThread();
// Stop Monitor
// IsMonitoring()
~Foo() {
if (foo_thread.joinable()) {
foo_thread.join();
}
};
private:
int private_method(int a) { return a + bar; };
int foo;
int bar;
std::thread foo_thread;
void threadFunction();
funcPtr myFunc;
std::atomic<bool> monitoring = ATOMIC_VAR_INIT(false);
};
void Foo::StartThread() {
foo_thread = std::thread(threadFunction, this);
}
void Foo::threadFunction() {
for (int i = 0; i < 10; i++) {
std::cout << "Hello:" << i << std::endl;
Sleep(500);
}
}
I figured it out, proper syntax to make this work is:
foo_thread = std::thread(&Foo::threadFunction, this);
I had been writing a simple game on Arduino Uno using Tinkercad's simulator. I created a class named Bullet and a class named MainCharacter, I put it directly into the code editor of Tinkercad since it cannot be imported multiple .h and .cpp files like a normal Arduino compiler.
class Bullet{
private:
int xLocation;
int yLocation;
int flySpeed;
bool isBetween;
public:
Bullet(int flySpeed){
this->flySpeed = flySpeed;
}
int getX() { return xLocation;}
int getY() { return yLocation;}
int getFlySpeed() { return flySpeed;}
bool getIsBetween() { return isBetween;}
void setX(int xLocation) { this->xLocation = xLocation;}
void setY(int yLocation) { this->yLocation = yLocation;}
void setIsBetween(bool isBetween) { this->isBetween = isBetween;}
};
class MainCharacter{
private:
int xLocation;
int yLocation;
public:
MainCharacter() {}
int getX() { return xLocation;}
int getY() { return yLocation;}
void setX(int xLocation){ this->xLocation = xLocation;}
void setY(int yLocation){ this->yLocation = yLocation;}
};
This is how my code would look like (simplified)
#include <LiquidCrystal.h>
#define ... //something
class Bullet{//above};
class MainCharacter{//above};
int game(){ //something};
Bullet spawnBullet(int flySpeed, int locationY)
{
Bullet bl = Bullet(flySpeed);
bl.setY(locationY);
bl.setIsBetween(true);
bl.setX(LENGTH_X_MAX);
return bl;
}
void setup{
//something
static MainCharacter mc = MainCharacter();
mc.setX(1);
mc.setY(0);
}
void loop{
game();
}
When I compiled, I received the error 'Bullet' does not name a type point to the spawnBullet function. I don't understand why the compiler didn't accept a function which returned a user-defined class in this case, while the int game() could work. I have tried to move the class sections around, above the #include, or under the setup section (if I delete out the spawnBullet() function, things worked perfectly, as you can see the MainCharacter constructor call under setup() still functioning well).
Very appreciate who can point out my mistake. I have spent an entirely evening for this dumb error and I am hopeless now.
This is the link for my Tinkercad project: https://www.tinkercad.com/things/fWqGvbNHamj-terrific-wluff/editel?sharecode=c2_J2v4jdWwJRrVpS7k3-LPJlEf_F5z082RSwWXUhY4
I've never used tinker cad before, but I managed to get your project to compile.
Here's my change to the declaration of spawnBullet(), this more or less comes from C, even though some c++17 features like auto do work in tinker cad.
class Bullet spawnBullet(int flySpeed, int locationY)
{
//...
}
The main .ino file of (classic) Arduino projects being preprocessed to generate a .cpp file, I think the problem comes from the preprocessor used by autodesk.
I would expect such an error should not be present in tinker cad, but this workaround should at least allow you to advance on your project. This is also probably not compatible with other arduino preprocessors, so you should expect to have to modify the code if you move development of the project to a local computer.
This question already has answers here:
Use class member functions as callbacks?
(6 answers)
Closed 3 years ago.
Arduino's attachInterrupt requires a callback function of type void(*)(), but I'd like to pass it a member function instead. I can't use a C++ member function here because of its implicit this argument.
Background
I know it's possible to use C++ member functions as callbacks. For example, FreeRTOS' xTaskCreate(...) takes a callback function of type void(*)(*).
isocpp.org has a nice FAQ on the use of member functions as callbacks.
In this related question user thiton writes:
Most sane callback libraries allow you to pass this void* argument to the functions as a way to have user-defined data in it
Perhaps the Arduino library is not "sane?" or perhaps this is design decision made to simplify the Arduino API?
it's there... inside attachInterrupt
I'm programming for an ESP32. In the arduino-esp32 implementation of attachInterrupt, there's a function called __attachInterruptFunctionalArg(...) that seems to do exactly what I want, but since it's not part of the Arduino API, I'm hesitant to include it in a project that's for public consumption because it may break.
Example program
// An attempt to summarize https://github.com/pierremolinaro/acan2517/issues/4
#include <stdio.h>
#include <stdint.h>
#include <functional>
#define IRAM_ATTR __attribute__((section(".iram1")))
// from `esp32-hal-gpio.c`
typedef void (*voidFuncPtrArg)(void*);
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type, bool functional);
// from Arduino `FunctionalInterrupt.cpp`
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode);
void IRAM_ATTR interruptFunctional(void* arg);
// from Arduino `FunctionalInterrupt.h`
struct InterruptArgStructure {
std::function<void(void)> interruptFunction;
};
// from ACAN2517
class ACAN2517
{
public: ACAN2517 (const int interrupt_pin);
public: void begin (void (* inInterruptServiceRoutine) (void));
public: void begin_functional (void (* inInterruptServiceRoutine) (void *), void *);
public: void isr(void);
private: const int interrupt_pin;
};
ACAN2517::ACAN2517 (const int interrupt_pin):
interrupt_pin(interrupt_pin)
{};
#define FALLING 0
// This won't work with a member function
void ACAN2517::begin (void (* inInterruptServiceRoutine) (void)) {
attachInterrupt(interrupt_pin, inInterruptServiceRoutine, FALLING);
}
// This will, but is prone to breakage when the Arduino internals change
void ACAN2517::begin_functional (void (* inInterruptServiceRoutine) (void *), void *arg)
{
__attachInterruptFunctionalArg(interrupt_pin, inInterruptServiceRoutine, arg, FALLING, true);
}
void ACAN2517::isr(void)
{
printf("fhtagn");
}
//===
// User code begin
//===
#define N_DRIVERS 3
ACAN2517 g_driver(23); // Initializing a driver instance statically
ACAN2517 *drivers[N_DRIVERS];
void call_ACAN_isr(void *arg)
{
ACAN2517 *driver = (ACAN2517 *)arg;
driver->isr();
}
int main()
{
g_driver.begin( []{g_driver.isr();} ); // No problem
for (int i = 0; i < N_DRIVERS; i++)
{
drivers[i] = &ACAN2517(i);
drivers[i]->begin( []{drivers[i]->isr();} );
// ERROR
// static void lambda []void ()->void::_FUN()
// an enclosing-function local variable cannot be referenced in a lambda body unless it is in the capture list
}
for (int i = 0; i < N_DRIVERS; i++)
{
drivers[i] = &ACAN2517(i);
drivers[i]->begin( [i]{drivers[i]->isr();} );
// ERROR
// no suitable conversion function from "lambda []void ()->void" to "void (*)()" exists
}
for (int i = 0; i < N_DRIVERS; i++)
{
drivers[i] = &ACAN2517(i);
ACAN2517 *driver = drivers[i];
drivers[i]->begin_functional( [driver]{driver->isr();}, driver);
// Not sure how to get this to work in a lambda...
}
for (int i = 0; i < N_DRIVERS; i++)
{
drivers[i] = &ACAN2517(i);
ACAN2517 *driver = drivers[i];
drivers[i]->begin_functional( call_ACAN_isr, driver);
// OK
}
}
//===
// User code end
//===
// from esp32-hal-gpio.c
extern void __attachInterruptFunctionalArg(uint8_t pin, voidFuncPtrArg userFunc, void * arg, int intr_type, bool functional)
{
// ...
}
// from Arduino `FunctionalInterrupt.cpp`
void attachInterrupt(uint8_t pin, std::function<void(void)> intRoutine, int mode)
{
// use the local interrupt routine which takes the ArgStructure as argument
__attachInterruptFunctionalArg (pin, (voidFuncPtrArg)interruptFunctional, new InterruptArgStructure{intRoutine}, mode, true);
}
void IRAM_ATTR interruptFunctional(void* arg)
{
InterruptArgStructure* localArg = (InterruptArgStructure*)arg;
if (localArg->interruptFunction)
{
localArg->interruptFunction();
}
}
I can't use a C++ member function here because of its implicit this argument.
Yes, that is exactly the problem and this can't be solved without extra code, if your API did not provide something what lets you store some additional data like the this pointer.
What you simply can do is:
Write your own wrapper and register the callback to the original handler. But that creates another indirection which increases the latency.
The other way is not as simple, but a bit less slow:
Write your own interrupt handler and callback registration. As you have the original sources of the arduino libs, you simply can replace the stuff around the attachInterrupt function.
Sorry, but there is no magic way to generate a data store for this without any additional software.
I had two projects which are written by C/C++.
Project 1 output is exe file and named MyProject.
Project 2 output is dll file and named Bridge.
I try to let Bridge to execute the function in MyProject. The function seems work, but I encounter an error
"Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention."
I doubt that the root cause may happen in __cdecl __stdcall convention, but still don't know how to solve it. All the project's Calling convention setting are __cdecl(/Gd), and VS IDE is VS 2013.
DLL Project (Bridge) Code
Header
typedef void(*sfcTrace)(const char *logLevel, const char *logMessage);
class OnRequestHandler
{
public:
virtual void writeLog(sfcTrace callback) = 0;
};
class GenericInfoHandler : public OnRequestHandler
{
public:
GenericInfoHandler();
~GenericInfoHandler() { delete this; };
void writeLog(sfcTrace callback);
};
extern "C" __declspec (dllexport) OnRequestHandler* __cdecl oneBridgeCallBack()
{
return new GenericInfoHandler;
}
CPP
void GenericInfoHandler::writeLog(sfcTrace callback)
{
const char *level = "DEBUG";
const char *message = "TEST";
callback(level, message);
}
MyProject Source code:
typedef OnRequestHandler* (__cdecl *test)();
HINSTANCE getDLL = LoadLibrary("Bridge.dll");
if (!getDLL)
{
cout << "Cannot not load DLL." << endl;
}
test func = (test)::GetProcAddress(getDLL, "oneBridgeCallBack");
if (!func)
{
cout << "Cannot not locate the function." << endl;
}
OnRequestHandler* instance = func();
instance->writeLog(&MyProject::TestCallBack); <----- Error Occurs here
Function implementation in MyProject
void MyProject::TestCallBack(const char *level, const char *message)
{
if (strcmp(level, "INFO") == 0){
// do something
}
else if (strcmp(level, "DEBUG") == 0){
// do something
}
}
Header:
typedef void(MyProject::*TestCallBack)(const char *logLevel, const char *logMessage);
class OnRequestHandler
{
public:
virtual void writeLog(sfcTrace callback) = 0;
};
class GenericInfoHandler : public OnRequestHandler
{
public:
GenericInfoHandler();
~GenericInfoHandler() { delete this; };
void writeLog(sfcTrace callback);
};
Try the following: (I am presuming that MyProject::TestCallback is not a static method, and it is it's use as a plain function that causes the stack corruption)
Update
typedef void(*sfcTrace)(const char *logLevel, const char *logMessage); to take an additional void * argument that will be supplied alongside the callback. I.e.:
typedef void(*sfcTrace)(void *cb_arg, const char *logLevel, const char *logMessage);
^^^^^^^^^^^^
Then update OnRequestHandler::writeLog(sfcTrace callback) and its overrides to take the additional argument from the caller. I.e.,
virtual void OnRequestHandler::writeLog(sfcTrace callback, void *cb_arg) = 0;
^^^^^^^^^^^^
void GenericInfoHandler::writeLog(sfcTrace callback, void *cb_arg);
^^^^^^^^^^^^
Update the implementation of writeLog to pass this new arg to the callback:
void GenericInfoHandler::writeLog(sfcTrace callback, void *cb_arg)
{ ^^^^^^^^^^^^
const char *level = "DEBUG";
const char *message = "TEST";
callback(cb_arg, level, message);
} ^^^^^^
Now we can write a new non-member callback function that is capable of calling a MyProject object's methods, so long as a MyProject object is passed via cb_arg:
void myprj_method_callback(void *arg, const char *logLevel, const char *logMessage) {
MyProject *mp = (MyProject *)arg;
mp->TestCallBack(level, message);
}
Finally, we can update the call to pass the new call-back function, and argument:
instance->writeLog(myprj_method_callback, (void *)this);
As an aside, it is generally good practice on all callbacks to let the callback supplier pass an argument that will in turn be passed to the callback when it is called. This lets the user of a callback mechanism convey the relevant data-structures to the callback function without having to store them in global variables.
I'm trying to make Wrapper class for FreeRTOS on ARM GNU compiler with C++. I did it on Keil MDK-ARM v5.14 without any problem but I cant accomplish on eclipse platform. I think it is about my wrong Source Location configuration on eclipse.
I'm using Eclipse Luna 4.4.2, ARM GNU GCC 4.9.2 and GNU ARM Eclipse Plug-ins
Here is my Wrapper class interface:
#ifndef TASKOOP_H_
#define TASKOOP_H_
#include "FreeRTOS.h"
#include "task.h"
class TaskOOP {
private:
TaskHandle_t _handle;
public:
TaskOOP();
virtual ~TaskOOP();//xTaskCreate( vTaskCode, "NAME", STACK_SIZE, NULL, tskIDLE_PRIORITY, &xHandle );
void create(const char * const pcName, const uint16_t usStackDepth, UBaseType_t uxPriority); //xTaskCreate( pvTaskCode, pcName, usStackDepth, pvParameters, uxPriority, pxCreatedTask )
void deleteT(); //vTaskDelete( TaskHandle_t xTaskToDelete )
void suspend(); //vTaskSuspend( TaskHandle_t xTaskToSuspend )
void resume(); //vTaskResume( TaskHandle_t xTaskToResume )
UBaseType_t getTaskPriority(); //uxTaskPriorityGet( TaskHandle_t xTask )
void setTaskPriority(UBaseType_t uxNewPriority); //vTaskPrioritySet( TaskHandle_t xTask, UBaseType_t uxNewPriority )
static void delay(const TickType_t xTicksToDelay); //vTaskDelay( const TickType_t xTicksToDelay )
static void vTaskStartScheduler( void );
static void vTaskEndScheduler( void );
static void vTaskSuspendAll( void );
static BaseType_t xTaskResumeAll( void );
private:
static void bridge(void* pThis);
protected:
virtual void run(void) = 0;
};
#endif /* TASKOOP_H_ */
Here is my implementation:
#include "TaskOOP.h"
TaskOOP::TaskOOP():_handle(0) {
// TODO Auto-generated constructor stub
}
TaskOOP::~TaskOOP() {
// TODO Auto-generated destructor stub
}
void TaskOOP::create(const char * const pcName, const uint16_t usStackDepth, UBaseType_t uxPriority){
xTaskCreate(bridge,pcName,usStackDepth,this,uxPriority,&_handle);
}
void TaskOOP::deleteT(){
vTaskDelete(_handle);
}
void TaskOOP::suspend(){
vTaskSuspend(_handle);
}
void TaskOOP::resume(){
vTaskResume(_handle);
}
UBaseType_t TaskOOP::getTaskPriority(){
return uxTaskPriorityGet(_handle);
}
void TaskOOP::setTaskPriority(UBaseType_t uxNewPriority){
vTaskPrioritySet( _handle, uxNewPriority );
}
void TaskOOP::delay(const TickType_t xTicksToDelay){
vTaskDelay(xTicksToDelay);
}
void TaskOOP::vTaskStartScheduler(void){
vTaskStartScheduler();
}
void TaskOOP::vTaskEndScheduler(void){
vTaskEndScheduler();
}
void TaskOOP::vTaskSuspendAll(void){
vTaskSuspendAll();
}
BaseType_t TaskOOP::xTaskResumeAll( void ){
return xTaskResumeAll();
}
void TaskOOP::bridge(void* pThis){
TaskOOP* task = (TaskOOP *)pThis;
task->run();
}
Here are some print-screens from my eclipse settings:
I cant upload them as image because I dont have enough reputation.
So the question is how should I configure my Path and Source Location?