0

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

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

Error

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

Code

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*>(buffer), sizeof(buffer), priority);
        }
        
    }
    catch(const std::exception& 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 = "192.168.1.101";
    Ipc<sensor_data_t> i{ "test" };
    i.send(data);
}
Dark Sorrow
  • 1,681
  • 14
  • 37
  • 1
    This question's shown code fails to meet Stackoverflow's requirements for showing a [mre]. Because of that it's unlikely that anyone here can conclusively answer the question; but only guess at the most. You need to [edit] your question to show a minimal example, no more than one or two pages of code (the "minimal" part), that everyone else can cut/paste ***exactly as shown***, compile, run, and reproduce the described issue (the "reproducible" part, this includes any ancillary information, like any input to the program). See [ask] for more information. – Sam Varshavchik Jul 31 '23 at 11:34
  • You are trying to cast value to pointer, it's not allowed – Dmitry Jul 31 '23 at 11:36
  • @Dmitry, I get what you are saying but why am I getting error `error C2440: 'const_cast': cannot convert from 'const T' to 'T'`. – Dark Sorrow Jul 31 '23 at 11:40
  • @DarkSorrow `const_cast` can only cast between reference and pointer types, not 'value types'. – chrysante Jul 31 '23 at 11:43
  • Also if you send the `sensor_data_t` object to another process, this won't send the allocated buffer of the `std::string` member. So reading the string from the recieving process would most likely crash, if your string is too long for the small buffer optimization. You should restrict `T` to be trivially copyable. – chrysante Jul 31 '23 at 11:47
  • Also why this: `if (communication_module == true) {...}`? How can `communication_module` ever not be true? – chrysante Jul 31 '23 at 11:48
  • @chrysante communication_module == true is only used for minimal reproducible example. In real application the value of `communication_module` will be set in each process appropriately. – Dark Sorrow Jul 31 '23 at 11:50
  • @chrysante, In gcc and clang is the small buffer optimization applied only if size of string is less than 25? What are the options available to me if I wish to send std::string or std::vector of size larger than 25? – Dark Sorrow Jul 31 '23 at 11:53
  • @chrysante Will std::array also cause me such problems as std::array are stored on stack? – Dark Sorrow Jul 31 '23 at 11:54
  • 2
    Don't send std::string or std::vector. It will not work. Send C-style arrays. – n. m. could be an AI Jul 31 '23 at 11:57
  • `std::array` won't be a problem, as long as `T` is trivially copyable. If you want to send arbitrary length strings, send the character buffer directly, so instead of `sizeof(buffer)` as the size argument use the length of the string. – chrysante Jul 31 '23 at 11:57
  • @chrysante, thanks for your input. The problem I have is that my communication module is interfaced external hardware on one end and multiple different processes on other end. I'm interfacing with multiple hardware sending arbitrary length data/string. To make the matter worse same hardware can also send arbitrary length data/strings. – Dark Sorrow Jul 31 '23 at 12:03

1 Answers1

3

Your send function is declared as such:

template<typename T>
inline bool Ipc<T>::send(const T& 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 = "192.168.1.101";
    Ipc<sensor_data_t> i{ "test" };
    i.send(data);
}

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

Therefore, when you reinterpret_cast<void*>(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 & (address-of) operator to get the pointer to your buffer here.

Here's a revised version of your code:

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;
    }
}

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->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& 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.

RedStoneMatt
  • 465
  • 3
  • 11
  • Thanks your solution worked. Can you elaborate on your last paragraph? – Dark Sorrow Jul 31 '23 at 12:04
  • I updated my answer accordingly to your request. If my solution worked and suits your needs, please mark it as accepted using the check found below the vote counts ^^ – RedStoneMatt Jul 31 '23 at 12:20