将结构体转换为void * 在模板中的类型错误

huangapple go评论72阅读模式
英文:

casting error struct to void * in template

问题

我正在开发一个模板库,用于在我的应用程序中使用Boost.Interprocess作为基本依赖项进行IPC(进程间通信)。我遇到了下面提到的错误。

我主要打算通过类型转换(type punning)来处理结构体(struct),有时也会处理类(class)(通过二进制序列化)。

我将常量引用作为参数传递。在将其转换为错误之前,我尝试使用const_cast类型转换来删除const修饰符。

错误信息:

1>C:\Users\admin\source\repos\TestApplication\TestApplication\Ipc.h(49,1): error C2440: 'reinterpret_cast': 无法将类型'T'转换为'void *'
1>C:\Users\admin\source\repos\TestApplication\TestApplication\Ipc.h(53,1): error C2440: 'reinterpret_cast': 无法将类型'const T'转换为'void *'

代码:

template <typename T> class Ipc {
public:
    bool send(const T& buffer, std::uint32_t priority = 0);

    Ipc(const std::string& queue_name, bool communication_module = false, int message_max_number = 5) :
        mq_com_receive(boost::interprocess::open_or_create, (queue_name + "_receive").c_str(), message_max_number, sizeof(T)),
        mq_com_send(boost::interprocess::open_or_create, (queue_name + "_send").c_str(), message_max_number, sizeof(T)),
        communication_module(communication_module) {}

private:
    boost::interprocess::message_queue mq_com_receive;
    boost::interprocess::message_queue mq_com_send;
    bool communication_module;
};

template<typename T>
inline bool Ipc<T>::send(const T& buffer, std::uint32_t priority)
{
    bool communication_module = true;
    try
    {
        if (communication_module == true)
        {
            return this->mq_com_send.try_send(reinterpret_cast<void*>(const_cast<T*>(&buffer)), sizeof(buffer), priority);
        }
        else
        {
            return this->mq_com_receive.try_send(reinterpret_cast<void*>(const_cast<T*>(&buffer)), sizeof(buffer), priority);
        }

    }
    catch (const std::exception& ex)
    {
        //TODO 记录异常
        return false;
    }
}

main.cpp

typedef struct
{
    std::uint8_t id;
    std::string ip;
} sensor_data_t;

int main()
{
    sensor_data_t data;
    data.id = 1;
    data.ip = "192.168.1.101";
    Ipc<sensor_data_t> i{ "test" };
    i.send(data);
}

请注意,这是您提供的代码的翻译部分,不包括代码的其他部分。

英文:

I'm developing a template library for IPC in my application using Boost.Interprocess as base dependency.<br>
I'm getting the bellow mentioned error.<br>
I intend to mainly struct (by type punning) and some-times classes (by binary serialization).<br>

I'm passing const reference as a parameter.<br>
I tried removing const specifier using const_cast type conversion before converting it to

Error

1&gt;C:\Users\admin\source\repos\TestApplication\TestApplication\Ipc.h(49,1): error C2440: &#39;reinterpret_cast&#39;: cannot convert from &#39;T&#39; to &#39;void *&#39;
1&gt;C:\Users\admin\source\repos\TestApplication\TestApplication\Ipc.h(53,1): error C2440: &#39;reinterpret_cast&#39;: cannot convert from &#39;const T&#39; to &#39;void *&#39;

Code

template &lt;typename T&gt; class Ipc { 
public:
 	bool send(const T&amp; buffer, std::uint32_t priority = 0);  	

    Ipc(const std::string&amp; queue_name, bool communication_module = false, int message_max_number = 5) :  		mq_com_receive(boost::interprocess::open_or_create, (queue_name + &quot;_receive&quot;).c_str(), message_max_number, sizeof(T)),  			mq_com_send(boost::interprocess::open_or_create, (queue_name + &quot;_send&quot;).c_str(), message_max_number, sizeof(T)),
        				communication_module(communication_module) {}

private: 	
boost::interprocess::message_queue mq_com_receive; 	 
boost::interprocess::message_queue mq_com_send; 	
bool communication_module; };

template&lt;typename T&gt;
inline bool Ipc&lt;T&gt;::send(const T&amp; buffer, std::uint32_t priority)
{
    bool communication_module  = true;
	try
	{
		if (communication_module == true)
		{
			return this-&gt;mq_com_send.try_send(reinterpret_cast&lt;void*&gt;(const_cast&lt;T&gt;(buffer)), sizeof(buffer), priority);
		}
		else
		{
			return this-&gt;mq_com_receive.try_send(reinterpret_cast&lt;void*&gt;(buffer), sizeof(buffer), priority);
		}
		
	}
	catch(const std::exception&amp; ex)
	{
		//TODO Log Exception
		return false;
	}
}

main.cpp

typedef struct
{
	std::uint8_t id;
	std::string ip;
}sensor_data_t;


int main()
{
    sensor_data_t data;
    data.id = 1;
    data.ip = &quot;192.168.1.101&quot;;
    Ipc&lt;sensor_data_t&gt; i{ &quot;test&quot; };
    i.send(data);
}

答案1

得分: 3

你的“send”函数声明如下:

template<typename T>
inline bool Ipc<T>::send(const T& buffer, std::uint32_t priority)

这里的“buffer”参数的类型是“T”。然而,并没有保证“T”是一个指针类型。

而且,在你的“main”函数中:

int main()
{
    sensor_data_t data;
    data.id = 1;
    data.ip = "192.168.1.101";
    Ipc<sensor_data_t> i{ "test" };
    i.send(data);
}

“data”在这里的类型是“sensor_data_t”,它是一个值类型,而不是指针类型。

因此,当你进行“reinterpret_cast<void*>(&const_cast<T&>(buffer))”时,实际上是在尝试将一个值转换为指针,这是不可能的

要获取一个值的指针,你可以使用“&”(取地址)操作符来获取参数中的缓冲区的指针。

以下是你代码的修改版本:

template<typename T>
inline bool Ipc<T>::send(const T& buffer, std::uint32_t priority)
{
    bool communication_module = true;
    try
    {
        if (communication_module)
        {
            return this->mq_com_send.try_send(reinterpret_cast<void*>(&const_cast<T&>(buffer)), sizeof(buffer), priority);
        }
        else
        {
            return this->mq_com_receive.try_send(reinterpret_cast<void*>(&const_cast<T&>(buffer)), sizeof(buffer), priority);
        }
        
    }
    catch (const std::exception& ex)
    {
        // TODO Log Exception
        return false;
    }
}

这样,你可以获取参数中传递的缓冲区的指针,并将其转换为“void*”,假设你没有提供的函数的实现是正确的。

最后,请确保由“this->mq_com_send.try_send”调用的函数在函数返回之前立即复制缓冲区的内容并发送它们,而不仅仅是复制缓冲区的指针以供以后发送或其他操作,因为一旦你的“main”函数返回,你的缓冲区就会超出作用域。

编辑

你能详细解释一下你最后一段吗?

你在“main”函数中声明了“sensor_data_t data;”,在其中通过引用将“data”传递给了“i.send(T& buffer)”。

由于“sensor_data_t data;”是在“main”函数中创建的,它存在的时间与“main”函数不返回的时间相同,因为它将会超出作用域(“data”位于堆栈上)。

我们不知道“mq_com_send.try_send”的具体实现细节。因此,可能它不会立即发送缓冲区的内容,也不会复制内容以供以后发送。

一些网络库将缓冲区的指针保存在队列中,然后在与调用“try_send”函数的线程不同的线程中异步发送其内容(可能在数据发送后会有回调供你释放或进行其他操作)。

这意味着,如果情况是这样的话,那么缓冲区可能会在“main”返回之后才被发送,这可能是错误的来源,因为在那时,该缓冲区将已经无效。

因此,你应该确保你的“try_send”函数不会这样做,实际上要么立即发送缓冲区的内容,要么将缓冲区的副本排队以供后续处理。

英文:

Your send function is declared as such:

template&lt;typename T&gt;
inline bool Ipc&lt;T&gt;::send(const T&amp; buffer, std::uint32_t priority)

The buffer parameter here is of type T. However, there is no guarantee that T is a pointer.

And indeed, in your main:

int main()
{
    sensor_data_t data;
    data.id = 1;
    data.ip = &quot;192.168.1.101&quot;;
    Ipc&lt;sensor_data_t&gt; i{ &quot;test&quot; };
    i.send(data);
}

data here is of type sensor_data_t, which is a value type, not a pointer.

Therefore, when you reinterpret_cast&lt;void*&gt;(buffer), you essentially try to cast a value to a pointer, which is not possible.

To get the pointer of a value, you can use the &amp; (address-of) operator to get the pointer to your buffer here.

Here's a revised version of your code:

template&lt;typename T&gt;
inline bool Ipc&lt;T&gt;::send(const T&amp; buffer, std::uint32_t priority)
{
    bool communication_module = true;
    try
    {
        if (communication_module)
        {
            return this-&gt;mq_com_send.try_send(reinterpret_cast&lt;void*&gt;(&amp;const_cast&lt;T&amp;&gt;(buffer)), sizeof(buffer), priority);
        }
        else
        {
            return this-&gt;mq_com_receive.try_send(reinterpret_cast&lt;void*&gt;(&amp;const_cast&lt;T&amp;&gt;(buffer)), sizeof(buffer), priority);
        }
        
    }
    catch (const std::exception&amp; ex)
    {
        // TODO Log Exception
        return false;
    }
}

This way, you get the pointer to the buffer passed in parameter, and cast it to void*, which should work assuming that the functions you didn't send the content of are correct.

One last thing, make sure that the function called by this-&gt;mq_com_send.try_send actually copies the content of the buffer to send and sends them before the function returns rather than just copying the buffer's pointer for later send or whatnot, as your buffer will get out of scope as soon as your main function returns.

Edit

> Can you elaborate on your last paragraph?

You declared sensor_data_t data; in your main function, in which you called i.send(T&amp; buffer) by passing data to it by reference.

Since sensor_data_t data; has been created in your main function, it exists for as long as your main function does not return, because it'll get out of scope (data is on the stack).

We don't know the details of implementation of mq_com_send.try_send though.
So it is possible that it does not send the content of your buffer immediately, nor copies its content for a later send.

Some network libraries save the pointer to the buffer in a queue, to then send its content asynchronously, in another thread than the one you called your try_send function in (with a potential callback after the data has been sent for you to free it or whatever).

Which means that if this is the case, then it is possible that the buffer is sent after main returns, which can be the source of an error because said buffer will have been invalidated by that time.

So, you should make sure that your try_send function does not do this and in fact either sends the content of your buffer immediately, or queues a copy of the buffer that is handled separately.

huangapple
  • 本文由 发表于 2023年7月31日 19:32:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76803215.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定