我已经定义了两个数据结构,它们必须保持彼此相同的大小才能使应用程序正常运行.结构用于在PC和DSP之间进行通信.DSP代码位于"C",PC端是C++.
例如:
struct inbound_data{
int header[5];
float val1;
float val2;
int trailer[3];
};
struct outbound_data{
int header[5];
int reply1;
int reply2;
float dat1;
float dat2;
int filler[1];
}
Run Code Online (Sandbox Code Playgroud)
稍后我会做类似的事情:
int tx_block[sizeof(outbound_data)];
int rx_block[sizeof(inbound_data)];
Run Code Online (Sandbox Code Playgroud)
这些阵列将被传递到通信外围设备,以在设备之间进行发送和接收.
由于硬件的工作原理,两个结构的大小必须匹配,因此缓冲区的大小相同.这很容易确保适当的关注,但偶尔通过设计周期,数据结构会被修改.如果一个人不是非常小心,并且意识到结构保持相同尺寸的要求(并且也反映在PC侧代码中),则会发生混乱.
我想找到一种编译时方法,如果其中一个结构被修改,那么代码就不会构建,这样它就不会与其他结构的大小相匹配.
在"标准"C中,这可能以某种方式在编译时检查大小,如果它们不同则会失败吗?(我认为我的编译器至少是C99,可能不是11).
在C++中,我想测试传递给函数的值是否为非零,并在该条件下基于某些行为.
例如:
void do_something(float x){
if(x) // <-- prefer this format?
do_a();
else
do_b();
}
Run Code Online (Sandbox Code Playgroud)
VS:
void do_something(float x){
if(x != 0) // <-- or this format?
do_a();
else
do_b();
}
Run Code Online (Sandbox Code Playgroud)
其他形式:
void do_something(int x){
x? do_a() : do_b(); // <-- prefer this?
x!=0? do_a() : do_b(); // <-- or this?
}
Run Code Online (Sandbox Code Playgroud)
这些都是"形成良好",或者是否有某种原因在某些情况下会出现未定义的行为?
我在godbolt.org上测试过,两种形式都生成完全相同的汇编代码.我使用int,float,ternary运算符和if()进行了测试,并且在所有情况下,代码对于两种形式看起来都相同.
我目前倾向于使用if(x != 0)为float/ double,和if(x)为int,有些是由于不同的NaN的浮点值的复杂性,以及其它特殊值.
我正在实现一个'序列锁'类,以允许锁定写入和无锁读取数据结构.
包含数据的结构包含序列值,在写入发生时,该值将递增两次.在写作开始之前,写作完成之后一次.作者在读者之外的其他线程上.
这是包含数据副本的结构,序列值如下所示:
template<typename T>
struct seq_data_t
{
seq_data_t() : seq(0) {};
int seq; <- should this be 'volatile int seq;'?
T data;
};
Run Code Online (Sandbox Code Playgroud)
整个序列锁定类在循环缓冲区中保存此结构的N个副本.编写器线程总是在循环缓冲区中写入最旧的数据副本,然后将其标记为当前副本.写作是互斥锁定的.
读取功能不会锁定.它试图读取数据的"当前"副本.它在读取之前存储'seq'值.然后它读取数据.然后它再次读取seq值,并将其与第一次读取的值进行比较.如果seq值没有改变,则认为读取是好的.
由于写入线程可以在读取发生时更改"seq"的值,因此我认为seq变量应该标记为volatile,以便read函数在读取数据后将显式读取该值.
read函数如下所示:它将在除writer之外的线程上,也许还有几个线程.
void read(std::function<void(T*)>read_function)
{
for (;;)
{
seq_data_type<T>* d = _data.current; // get current copy
int seq1 = d->seq; // store starting seq no
if (seq1 % 2) // if odd, being modified...
continue; // loop back
read_function(&d->data); // call the passed in read function
// passing it our data.
//??????? could this …Run Code Online (Sandbox Code Playgroud) 我实现了一个类,该类使我可以将线程与条件变量进行同步。我发现有关notify_all应该在锁内还是在锁外完成的信息相互矛盾。我发现示例是双向构造的。
首先释放锁的参数是为了防止等待的线程在被通知释放后立即在互斥体上阻塞。
反对先释放锁的论点是断言,等待线程可能会丢失通知。
释放功能的两个版本在这里:
// version 1 - unlock then notify.
void release(int address = 1)
{
{
std::lock_guard<std::mutex> lk(_address_mutex);
_address = address;
}
_cv.notify_all();
}
// version 2 - notify then unlock
void release(int address = 1)
{
std::lock_guard<std::mutex> lk(_address_mutex);
_address = address;
_cv.notify_all();
}
Run Code Online (Sandbox Code Playgroud)
供参考,等待代码如下所示:
bool wait(const std::chrono::microseconds dur, int address = 1)
{
std::unique_lock<std::mutex> lk(_address_mutex);
if (_cv.wait_for(lk, dur, [&] {return _address == address; }))
{
_address = 0;
return true;
}
return false;
}
Run Code Online (Sandbox Code Playgroud)
是否存在等待线程丢失版本1中的通知的风险,在该版本中,互斥对象被允许在notify_all之前超出作用域?如果是这样,它将如何发生?(这对我来说并不明显,这是如何导致错过通知的。)
我可以清楚地看到在通知过程中保持互斥锁处于锁定状态如何导致等待线程立即进入等待状态。但这是一个小小的代价,如果它可以防止错过通知。
我可以做这个:
struct foo{
foo(std::array<double, 3>){}
foo(std::array<double, 4>){}
}
Run Code Online (Sandbox Code Playgroud)
我想这样做:
struct foo{
foo(double A[3]){}
foo(double A[4]){}
}
Run Code Online (Sandbox Code Playgroud)
当然,这是行不通的。
这涉及具有3和4元素双精度数组的遗留代码。我希望构造一个包含3个或4个元素数组中的4个double的类,并在从3个元素数组中构造时以恒定值初始化第4个double。当我从4个元素的数组初始化时,我只复制了4个元素。
因此,我想让构造函数识别:
double something[3];
Run Code Online (Sandbox Code Playgroud)
从:
double something_else[4];
Run Code Online (Sandbox Code Playgroud)
我能想到的最好的方法是向构造函数添加另一个参数以区分两者。
class foo{
foo(double A[4], bool only_3 = false){}
foo(double *A, size_t n = 4){}
}
Run Code Online (Sandbox Code Playgroud)
还有更好的主意吗?
(在时间允许的情况下,我将淘汰所有原始数组,但现在我必须处理它。)
下面的代码是来自https://www.cplusplus.com/reference/atomic/atomic/compare_exchange_weak/的compare_exchange_weak的示例
我不明白 Compare_exchange_weak while 循环内可以接受哪些类型的操作。
在示例中,只要compare_exchange 返回 false,他们就会将 newNode->next 值设置为 oldHead 指针。我不明白这怎么总是有效的。
如果另一个线程处于同一个循环中,并且它在我们设置 oldHead 指针的时间和在我们的线程上compare_exchange 成功的时间之间成功更改了 oldHead 指针,会发生什么情况?那么我们的 newNode 中就会有错误的头指针。我不明白为什么这是不可能的。
例如,如果我在设置 ->next 值后放置 sleep(5),或者在循环中进行一些长计算,这会起作用吗?
标记为“在这里可以安全做什么”的循环是我不明白的部分。
// atomic::compare_exchange_weak example:
#include <iostream> // std::cout
#include <atomic> // std::atomic
#include <thread> // std::thread
#include <vector> // std::vector
// a simple global linked list:
struct Node { int value; Node* next; };
std::atomic<Node*> list_head (nullptr);
void append (int val) { // append an element to the list
Node* oldHead = list_head;
Node* newNode …Run Code Online (Sandbox Code Playgroud)