-
Notifications
You must be signed in to change notification settings - Fork 9
Description
C++11 narrowing conversion error in ModbusTCP_unix.h on 32-bit Linux builds
Environment
- ModbusLib Version: 0.4.6
- Compiler: Clang 7.0.1
- Platform: Linux 32-bit (i686)
- Build System: CMake 4.0.2
- Target: Debian 9
Description
Building ModbusLib 0.4.6 for 32-bit Linux fails due to C++11 narrowing conversion errors in src/unix/ModbusTCP_unix.h
. The setTimeout()
method attempts to initialize a timeval
structure with uint32_t
values that are implicitly narrowed to __time_t
and __suseconds_t
, which are long
types (32-bit on i686 architecture).
This issue does not occur on 64-bit Linux builds because long
is 64-bit, allowing the implicit conversion without narrowing.
Error Output
In file included from /home/webadm/Projects/3rdparty-modbuslib/ModbusLib-0.4.6/src/unix/ModbusTcpServer_unix.cpp:25:
In file included from /home/webadm/Projects/3rdparty-modbuslib/ModbusLib-0.4.6/src/unix/ModbusTcpServer_p_unix.h:5:
/home/webadm/Projects/3rdparty-modbuslib/ModbusLib-0.4.6/src/unix/ModbusTCP_unix.h:33:23: error: non-constant-expression cannot be narrowed from type 'unsigned int' to '__time_t' (aka 'long') in initializer list [-Wc++11-narrowing]
.tv_sec = timeout / 1000,
^~~~~~~~~~~~~~
/home/webadm/Projects/3rdparty-modbuslib/ModbusLib-0.4.6/src/unix/ModbusTCP_unix.h:33:23: note: insert an explicit cast to silence this issue
.tv_sec = timeout / 1000,
^~~~~~~~~~~~~~
static_cast<__time_t>( )
/home/webadm/Projects/3rdparty-modbuslib/ModbusLib-0.4.6/src/unix/ModbusTCP_unix.h:34:24: error: non-constant-expression cannot be narrowed from type 'unsigned int' to '__suseconds_t' (aka 'long') in initializer list [-Wc++11-narrowing]
.tv_usec = (timeout % 1000) * 1000
^~~~~~~~~~~~~~~~~~~~~~~
/home/webadm/Projects/3rdparty-modbuslib/ModbusLib-0.4.6/src/unix/ModbusTCP_unix.h:34:24: note: insert an explicit cast to silence this issue
.tv_usec = (timeout % 1000) * 1000
^~~~~~~~~~~~~~~~~~~~~~~
static_cast<__suseconds_t>( )
2 errors generated.
Root Cause
The issue is in src/unix/ModbusTCP_unix.h
, lines 33-34, in the setTimeout()
method:
inline void setTimeout(uint32_t timeout)
{
timeval tv
{
.tv_sec = timeout / 1000, // uint32_t -> __time_t (long)
.tv_usec = (timeout % 1000) * 1000 // uint32_t -> __suseconds_t (long)
};
setsockopt(m_socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
}
On 32-bit systems:
uint32_t
isunsigned int
(32 bits)__time_t
islong
(32 bits, signed)__suseconds_t
islong
(32 bits, signed)
C++11 forbids narrowing conversions from unsigned to signed types in initializer lists, even when they're the same size.
Proposed Fix
Add explicit casts to make the conversions explicit and compiler-compliant:
inline void setTimeout(uint32_t timeout)
{
timeval tv
{
.tv_sec = static_cast<__time_t>(timeout / 1000),
.tv_usec = static_cast<__suseconds_t>((timeout % 1000) * 1000)
};
setsockopt(m_socket, SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
setsockopt(m_socket, SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
}
Testing
After applying this fix:
- ✅ Linux 32-bit build (i686) succeeds with Clang 7.0.1
- ✅ Linux 64-bit build (x86_64) continues to work correctly
- ✅ No functional changes - same behavior with explicit casts
- ✅ Library created:
libmodbus.a
(304,894 bytes, 417 exported functions)
Build command used:
cmake -DCMAKE_TOOLCHAIN_FILE=../toolchain-linux32.cmake \
-DCMAKE_BUILD_TYPE=Release \
-DBUILD_SHARED_LIBS=OFF \
-DMB_QT_ENABLED=OFF \
..
Impact
Affected Platforms:
- All 32-bit Linux builds with C++11-compliant compilers
- Any builds with
-Werror=narrowing
or-Wc++11-narrowing
enabled
Not Affected:
- 64-bit Linux builds (long is 64-bit, no narrowing occurs)
- Builds with relaxed narrowing warnings
Additional Information
This is a standards-compliance issue introduced by C++11's stricter rules for initializer lists. The fix follows C++ best practices by making type conversions explicit, improving code clarity and portability.
The explicit casts are safe because:
- The timeout value is divided/modulated, ensuring results fit in the target types
- The conversion is from unsigned to signed of the same size
- The values represent time in milliseconds/microseconds, which are always positive and within range