|
int&& is a rvalue reference. Until C++11 we only had lvalue references. In Nontemplate_universal_ref(x) , x is a lvalue, not an rvalue. Try the following:
template <typename ParamType>
void Template_universal_ref(ParamType&& param)
{
cout << "Template_universal_ref, param : " << param << endl;
}
void NonTemplate_universal_ref(int&& param)
{
cout << "NonTemplate_universal_ref(int&&), param : " << param << endl;
}
void NonTemplate_universal_ref(int& param)
{
cout << "NonTemplate_universal_ref(int&), param : " << param << endl;
}
int main()
{
int x=5;
Template_universal_ref(x);
NonTemplate_universal_ref(x);
NonTemplate_universal_ref(x+3);
return 0;
} Note that in the second call to NonTemplate_universal_ref, the argument x+3 is not an lvalue, that is, it cannot be assigned to.
Now, maybe someone can explain why the call via the template works?
|
|
|
|
|
I am working with embedded programming in standard C and there are 2 microcontrollers communicating with each other using a home-made SPI-protocol. In addition, microcontroller 2 has a CAN-bus that should also (indirectly) be fully accessible from microcontroller 1. The scheduler and task queues are also home-made so no realtime operating system is used. My tasks look like this:
void (*task_t)(uint8_t* taskData, uint16_t sizeOfTaskDataInBytes); My functions to add 1 or 2 tasks look like this:
typedef enum {
TASK_IN_WAIT_PHASE = 0,
TASK_FINISHED = 1
} taskStatus_e;
typedef taskStatus_e (*getTaskStatusCallback_t)(uint8_t* data, uint16_t dataSizeBytes);
Bool_t queueTask(taskQueueSelector_e, task_t, uint8_t* data, uint16_t dataSizeBytes, getTaskStatusCallback_t);
Bool_t queue2Tasks(taskQueueSelector_e, task_t task1, uint8_t* data1, uint16_t data1SizeBytes, getTaskStatusCallback_t getTaskStatusCallback1, task_t task2, uint8_t* data2, uint16_t data2SizeBytes, getTaskStatusCallback_t getTaskStatusCallback2); When microcontroller 1 wants to read from microcontroller 2 over SPI, the following code is used:
struct SPI_secretData_s {
volatile Bool_t accessInProgress;
volatile int8_t numRetriesLeft;
};
#define PLEASE_SEE_NUM_REGS_TO_ACCESS_PARAMETER (1)
struct SPI_readRegsTaskParams_s {
struct SPI_secretData_s secretData;
volatile uint32_t startAddr;
volatile uint16_t numRegsToAccess;
volatile SPI_readStatus_e readStatus;
volatile uint16_t readBuffer[PLEASE_SEE_NUM_REGS_TO_ACCESS_PARAMETER];
};
queue2Tasks(SPI_QUEUE_SELECTOR, SPI_readRegs, (uint8_t*)&SPI_readRegsTaskParams, sizeof(SPI_readRegsTaskParams), SPI_getTaskStatusCallback, SPI_handleReadIsFinished, NULL, 0, NULL);
taskStatus_e SPI_getTaskStatusCallback(uint8_t* data, uint16_t dataSizeBytes) {
struct SPI_readRegsTaskParams_s* SPI_readRegsTaskParams = (struct SPI_readRegsTaskParams_s*)data;
return (SPI_readRegsTaskParams->secretData.accessInProgress) ? TASK_IN_WAIT_PHASE : TASK_FINISHED;
}
uint32_t SPI_handleReadIsFinished(uint8_t* notUsed, uint16_t sizeOfNotUsedInBytes) {
struct SPI_readRegsTaskParams_s* SPI_readRegsTaskParams = (struct SPI_readRegsTaskParams_s*)getPreviousTaskData();
} When microcontroller 2 wants to read from the CAN-bus, the following code is used:
struct CAN_secretData_s {
volatile Bool_t accessInProgress;
};
struct CAN_readRegsTaskParams_s {
struct CAN_secretData_s secretData;
volatile uint32_t startAddr;
volatile uint8_t numRegsToReadMax4;
volatile uint16_t readRegsBuffer[4];
volatile CAN_readStatus_e readStatus;
};
queue2Tasks(CAN_QUEUE_SELECTOR, CAN_readRegs, (uint8_t*)&CAN_readRegsTaskParams, sizeof(CAN_readRegsTaskParams), CAN_getTaskStatusCallback, CAN_handleReadIsFinished, NULL, 0, NULL);
taskStatus_e CAN_getTaskStatusCallback(uint8_t* data, uint16_t dataSizeBytes) {
struct CAN_readRegsTaskParams_s* CAN_readRegsTaskParams = (struct CAN_readRegsTaskParams_s*)data;
return (CAN_readRegsTaskParams->secretData.accessInProgress) ? TASK_IN_WAIT_PHASE : TASK_FINISHED;
uint32_t CAN_handleReadIsFinished(uint8_t* notUsed, uint16_t sizeOfNotUsedInBytes) {
struct CAN_readRegsTaskParams_s* CAN_readRegsTaskParams = (struct CAN_readRegsTaskParams_s*)getPreviousTaskData();
}
Could someone please suggest how I should handle the case when microcontroller 1 wants to read from the CAN-bus, via microcontroller 2?
modified 8-Feb-19 5:20am.
|
|
|
|
|
Just like I have a secretData_s struct as part of the task data, maybe I should also have a postTaskInfo_s section:
#define PLEASE_SEE_SIZE_OF_POST_TASK_DATA_PARAMETER (1)
struct postTaskInfo_s {
task_t postTask;
taskQueueSelector_e postTaskQueueSelector;
uint16_t sizeOfPostTaskData;
uint8_t postTaskData[PLEASE_SEE_SIZE_OF_POST_TASK_DATA_PARAMETER]
}; Then if postTaskInfo->postTask is not null then the CAN_readRegs-task will queue the postTask just before it's finished. Can someone think of something better?
modified 8-Feb-19 5:58am.
|
|
|
|
|
I am working with embedded programming in standard C and there are 2 microcontrollers communication with each other using a home-made protocol. The scheduler and task queues are also home-made so no realtime operating system is used. My tasks look like this:
void (*task_t)(uint8_t* taskData, uint16_t sizeOfTaskDataInBytes); My add-task-to-queue-function looks like this:
Bool_t addTaskToQueue(queueSelector_e, task_t, uint8_t* taskData, uint16_t sizeOfTaskDataInBytes); Please note that the taskData is also inserted into the queue (in an area called task data pool area) so it's not just a pointer to taskData that is being queued. The task data to write data from microcontroller 1 to microcontroller 2 needs to look something like this:
struct writeRegTaskData_s {
uint32_t startAddr;
uint16_t numRegsToWrite;
uint8_t dataToWrite[numRegsToWrite]; or
uint8_t* dataToWrite } The problem is with the dataToWrite parameter. Sometimes a lot of data will be written, for example, during a firmware upgrade, and then it's overkill to put all the dataToWrite into the queue, it's perfectly fine to have one static buffer. At other times, for example when reacting to different push button events, I want to put my dataToWrite on the stack and then add it into my task queue. However, when I do this, I must make sure dataToWrite points to the proper location inside my task queue data pool and not on the original buffer on the stack. What's the recommended way to solve this issue in a clean way?
|
|
|
|
|
arnold_w wrote: I must make sure dataToWrite points to the proper location inside my task queue data pool and not on the original buffer on the stack. Then you need to allocate a block of memory from the pool, and copy the data there. I assume that you have a pool control mechanism that operates similar to malloc to manage the pool. In either case you pass the buffer pointer to the function that handles the data. You just need to make sure that pool buffers are released after they have been used.
|
|
|
|
|
Yes, the task data pools (one for each task queue) are simple FIFO:s, but they're kept separate from the queues where I store the task function pointers. The only thing I can think of is to place the copied data immediately after the task data struct:
typedef enum {
USE_STATIC_NON_QUEUED_WRITE_DATA = 0,
DATA_TO_WRITE_APPEARS_AFTER_STRUCT = 1
} writeBufferLocationOption_e;
struct writeRegTaskData_s {
uint32_t startAddr;
uint16_t numRegsToWrite;
writeBufferLocationOption_e writeBufferLocationOption;
uint8_t* dataToWrite }
Bool_t addTaskToQueue(queueSelector_e, task_t, uint8_t* taskData1, uint16_t sizeOfTaskDataInBytes1, uint8_t* taskData2, uint16_t sizeOfTaskDataInBytes2); When I do want to queue the data, then I'd pass the data as the taskData2 parameter. Can someone think of a cleaner solution?
|
|
|
|
|
What about something like:
struct writeRegTaskData_s {
writeBufferLocationOption_e writeBufferLocationOption;
uint16_t numRegsToWrite;
uint8_t dataToWrite[1]; }
You can then create your fixed buffer with the above structure, followed immediately by the data space. For the dynamic buffers you just need to calculate the total space required (struct plus data), allocate it and fill in the struct values, and copy the data. You then just have a single pointer to pass to your data maniulation routines.
|
|
|
|
|
I don't think I can guarantee that the static non-queued buffers can be put in the end of the structs because most of the time the structs will be created on the stack. Therefore, I think I need pointers to the buffers.
I also have a CAN-bus interface and typically the receive packet task would take a struct that looks like this:
struct packet_s {
uint32_t extID;
uint8_t DLC; uint8_t* data;
}; In my application these packets will always be queued (they never use static buffers) and as soon as they are queued the data pointer becomes worthless. The best thing I can think of is the following (a slight variation of Richard MacCutchan's suggestion):
#define PLEASE_SEE_DLC_FOR_PACKET_SIZE (1)
struct packet_s {
uint32_t extID;
uint8_t DLC; uint8_t data[PLEASE_SEE_DLC_FOR_PACKET_SIZE];
};
modified 4-Feb-19 9:27am.
|
|
|
|
|
When you declare your static buffers you just add the space required for the struct at the beginning. Then you only need one pointer whether it is static or dynamic. Keep things simple as much as possible.
|
|
|
|
|
Oh, wow, that's a pretty neat trick! Thanks for the suggestion.
|
|
|
|
|
|
|
The following is an example of a simple struct union:
typedef union _UINT32_PART_ {
uint32_t dwWord;
struct {
uint16_t dwMSB;
uint16_t dwLSB;
} hw;
struct {
uint8_t byMSB;
uint8_t byMSBL;
uint8_t byLSBH;
uint8_t byLSB;
} b;
} DWORD_PART;
Is is possible to somehow omit, for example dwMSB and byLSBH, without making the subsequent members point to the wrong things? So, basically this is what I want:
typedef union _UINT32_PART_ {
uint32_t dwWord;
struct {
uint16_t dwLSB; } hw;
struct {
uint8_t byMSB;
uint8_t byMSBL;
uint8_t byLSB; } b;
} DWORD_PART;
|
|
|
|
|
No, and what would be the point?
|
|
|
|
|
To divide a struct into a high-level application level part and a low-level driver part.
|
|
|
|
|
Sorry, but I do not understand what you hope to achieve. A struct is just a description of a block of memory that contains a fixed set of byte s, word s etc. If it is 20 byte s long then you must describe each part exactly so the compiler can calculate the offsets correctly. Leaving blank space and expecting the compiler to guess what is missing makes no sense.
|
|
|
|
|
Yes, but just like you can choose to only initialize parts of a struct:
struct demo_s {
int first;
int second;
int third;
};
struct demo_s demo2 = { .second = 2, .third = 3 }; I was hoping that some union names (hw or b, in the example in my first post) could omit having a reference.
|
|
|
|
|
Whether you initialise all or part of the struct before using it does not matter, except for your code. But you must still define it exactly and completely.
|
|
|
|
|
Yes, but only struct members relevant to the application level should be initialized at the application level. The low-level driver struct members should be initialized at the low-level driver level.
|
|
|
|
|
That is irrelevant; this discussion is about the struct definition. What you do with the content is dependent on your code, but the definition (as I keep repeating) must include every element of the block of memory.
|
|
|
|
|
|
Your first union does the job.
So maybe you give "uint8_t byLSBH;"another name to make it clear it is not relevant?
|
|
|
|
|
I like C but I feel it's Achilles heel is string processing. I've started to do a lot of parsing of text databases in arbitrary format without documentation lately and I need to adapt.
What I need to do is define patterns - expected format for the data and to store the values only if the whole string matches that known pattern. Input validation.
I'd rather not run the rest of my code without verifying the input conforms.
I think regular expressions are the best way to augment my existing skills without learning a new language, but regexes seem kind of varied and mixed breed.
Perl (5?) Seems to have formal standardization of regexes which is supported in many searching and text editing programs. There's also PCRE which I can compile on windows or download precompiled lib/dll.
Should I learn Perl regexes and use PCRE or am I overlooking things?
|
|
|
|
|
There are many websites that help to learn regexes, Expresso Regular Expression Tool[^] is a popular one. But you will also need a support library, as C does not have native support for them.
|
|
|
|
|
As far as I can tell there are at least three main types; POSIX basic, POSIX extended, and Perl Compatible.
There's a list of engines here:
Comparison of regular expression engines - Wikipedia[^]
And apparently some differences between PERL and PCRE:
Perl Compatible Regular Expressions - Wikipedia[^]
I don't know/understand if let's say 80 or 97 percent of the Regex syntax is the same between one version or another or if they are distinct subtypes with significant differences.
I don't know if they all support ascii, Unicode, and utf encoding, or whether they are all capable of returning matched variables or if some are only providing a match/no match result.
|
|
|
|
|