Skip to content

Commit abb6bce

Browse files
authored
Merge pull request #1010 from intel/push-2026-04-06
Push 2026 04 06
2 parents 0e2a113 + aab5dd8 commit abb6bce

File tree

4 files changed

+150
-23
lines changed

4 files changed

+150
-23
lines changed

AGENTS.md

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Agent-Assisted Code Improvements
2+
3+
This document tracks code improvements made with the assistance of AI agents.
4+
5+
> **Note:** For compilation instructions, see [README.md](README.md).
6+
7+
## MSVC Compilation Error Fix - numeric_limits::max() Macro Conflict
8+
9+
**Commit:** 2e387a11
10+
**Date:** 2026-04-06
11+
**File:** `src/pcm-sensor-server.cpp`
12+
13+
### Issue
14+
15+
MSVC compilation error caused by macro conflict with `std::numeric_limits<T>::max()`
16+
17+
The error occurred at two locations in the argument parsing code (the `-p` port and `-D` debug level options):
18+
19+
```cpp
20+
// Original code (problematic)
21+
if ( val > std::numeric_limits<unsigned short>::max() )
22+
throw std::out_of_range( "port out of range" );
23+
```
24+
25+
### Root Cause
26+
27+
Windows headers (particularly `windows.h` and related headers) often define `max` and `min` as preprocessor macros:
28+
29+
```cpp
30+
#define max(a,b) (((a) > (b)) ? (a) : (b))
31+
```
32+
33+
When the preprocessor encounters `std::numeric_limits<unsigned short>::max()`, the `max()` part gets expanded as a macro, resulting in malformed code that fails to compile.
34+
35+
### Solution
36+
37+
Wrap the function name in parentheses to prevent macro expansion:
38+
39+
```cpp
40+
// Fixed code
41+
if ( val > (std::numeric_limits<unsigned short>::max)() )
42+
throw std::out_of_range( "port out of range" );
43+
```
44+
45+
By adding parentheses around `max`, the preprocessor no longer recognizes it as a macro invocation, allowing the actual member function to be called.
46+
47+
### Technical Details
48+
49+
- **Affected code:** Port validation (`-p` option) and debug level validation (`-D` option)
50+
- **Context:** Command-line argument parsing for `-p` (port) and `-D` (debug level) options
51+
- **Alternative solutions:**
52+
1. `#undef max` before use (not recommended, affects entire translation unit)
53+
2. Define `NOMINMAX` before including Windows headers (project-wide change)
54+
3. Use `(std::numeric_limits<T>::max)()` (chosen solution - local and surgical)
55+
56+
### Why This Approach
57+
58+
The parentheses-wrapping approach is preferred because:
59+
1. **Minimal impact:** Only affects the specific call sites
60+
2. **No side effects:** Doesn't disable macros for other code that might depend on them
61+
3. **Clear intent:** Documents that macro expansion is being avoided
62+
4. **Standard practice:** Commonly used pattern in cross-platform C++ code
63+
64+
### Best Practices
65+
66+
When writing portable C++ code that may be compiled with Windows headers:
67+
- Always wrap standard library function names like `min`, `max` in parentheses when calling them
68+
- Consider using `(std::min)(a, b)` and `(std::max)(a, b)` for function calls, and `(std::numeric_limits<T>::max)()` for `numeric_limits`
69+
- Be aware that macros from platform headers can interfere with identically-named functions

src/debug.h

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,9 @@
66
#include <sstream>
77
#include <iomanip>
88
#include <iostream>
9+
#include <utility>
10+
#include <ctime>
11+
#include "types.h"
912

1013
#ifdef _MSC_VER
1114
#include <BaseTsd.h>
@@ -15,6 +18,9 @@
1518

1619
namespace pcm {
1720

21+
// Forward declaration
22+
std::pair<tm, uint64> pcm_localtime();
23+
1824
namespace debug {
1925
extern int currentDebugLevel;
2026

@@ -32,10 +38,10 @@ namespace debug {
3238
template<typename LVL, typename PF, typename F, typename L, typename... Args>
3339
void dyn_debug_output( std::ostream& out, LVL level, PF pretty_function, F file, L line, Args... args ) {
3440
std::stringstream ss;
35-
auto now = time(nullptr);
41+
auto tt = pcm_localtime();
3642
ss << "DBG(" << std::dec << level << "): File '" << file << "', line '" << std::dec << line << "' :\n";
3743
ss << "DBG(" << std::dec << level << "): " << pretty_function << ":\n";
38-
ss << "DBG(" << std::dec << level << ") " << std::put_time( localtime(&now), "%F_%T: " ); // Next code line will continue printing on this output line
44+
ss << "DBG(" << std::dec << level << ") " << std::put_time( &tt.first, "%F_%T: " ); // Next code line will continue printing on this output line
3945
dyn_debug_output_helper( ss, args... );
4046
out << ss.str() << std::flush;
4147
}

src/pcm-msr.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ int mainThrows(int argc, char * argv[])
141141
printf("SDL330 ERROR: Symlink/reparse point detected at '%s' (skipping write)\n", outfile.c_str());
142142
outflag = false;
143143
} else {
144-
ofile = fopen(outfile.c_str(),"w");
144+
if (fopen_s(&ofile, outfile.c_str(), "w") != 0) {
145+
ofile = NULL;
146+
}
145147
}
146148
#else
147149
// SDL330: Use O_NOFOLLOW to reject symlinks

src/pcm-sensor-server.cpp

Lines changed: 70 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,9 @@ constexpr unsigned int DEFAULT_HTTPS_PORT = DEFAULT_HTTP_PORT;
2929
typedef SOCKET socket_t;
3030
#define SHUT_RDWR SD_BOTH
3131
#define MSG_NOSIGNAL 0
32-
// errno values
33-
#define EAGAIN WSAEWOULDBLOCK
34-
#define EWOULDBLOCK WSAEWOULDBLOCK
32+
// PCM errno values mapped to Windows socket errors
33+
#define PCM_EAGAIN WSAEWOULDBLOCK
34+
#define PCM_EWOULDBLOCK WSAEWOULDBLOCK
3535
inline int close(SOCKET s) { return closesocket(s); }
3636
#else
3737
#include <unistd.h>
@@ -43,12 +43,16 @@ inline int close(SOCKET s) { return closesocket(s); }
4343
#include <sched.h>
4444
typedef int socket_t;
4545
#define INVALID_SOCKET (-1)
46+
// PCM errno values mapped to POSIX errors
47+
#define PCM_EAGAIN EAGAIN
48+
#define PCM_EWOULDBLOCK EWOULDBLOCK
4649
#define SOCKET_ERROR (-1)
4750
#endif
4851

4952
#include <cstring>
5053
#include <fstream>
5154
#include <ctime>
55+
#include <limits>
5256
#include <vector>
5357
#include <unordered_map>
5458

@@ -129,10 +133,17 @@ class datetime {
129133
public:
130134
datetime() {
131135
std::time_t t = std::time( nullptr );
136+
#ifdef _MSC_VER
137+
std::tm tm_buf;
138+
if (gmtime_s(&tm_buf, &t) != 0)
139+
throw std::runtime_error("gmtime_s failed");
140+
now = tm_buf;
141+
#else
132142
const auto gt = std::gmtime( &t );
133143
if (gt == nullptr)
134144
throw std::runtime_error("std::gmtime returned nullptr");
135145
now = *gt;
146+
#endif
136147
}
137148
datetime( std::tm t ) : now( t ) {}
138149
~datetime() = default;
@@ -184,9 +195,19 @@ class date {
184195
public:
185196
void printDate( std::ostream& os ) const {
186197
char buf[64];
198+
#ifdef _MSC_VER
199+
std::tm tm_buf;
200+
if (localtime_s(&tm_buf, &now) != 0)
201+
throw std::runtime_error("localtime_s failed");
202+
if (std::strftime(buf, 64, "%F", &tm_buf) == 0)
203+
throw std::runtime_error("Error writing date to buffer, too small?");
204+
#else
187205
const auto t = std::localtime(&now);
188-
assert(t);
189-
std::strftime( buf, 64, "%F", t);
206+
if (t == nullptr)
207+
throw std::runtime_error("std::localtime returned nullptr");
208+
if (std::strftime(buf, 64, "%F", t) == 0)
209+
throw std::runtime_error("Error writing date to buffer, too small?");
210+
#endif
190211
os << buf;
191212
}
192213

@@ -1274,7 +1295,7 @@ class basic_socketbuf : public std::basic_streambuf<CharT> {
12741295
switch ( sslError ) {
12751296
case SSL_ERROR_WANT_READ:
12761297
DBG( 3, "SSL_ERROR_WANT_READ: Errno = ", errno, ", strerror(errno): ", strerror(errno) );
1277-
if ( errno == EAGAIN || errno == EWOULDBLOCK ) {
1298+
if ( errno == PCM_EAGAIN || errno == PCM_EWOULDBLOCK ) {
12781299
DBG( 3, dbg, "Most likely the set timeout, so aborting..." );
12791300
close();
12801301
Base::setg( nullptr, nullptr, nullptr );
@@ -1288,7 +1309,7 @@ class basic_socketbuf : public std::basic_streambuf<CharT> {
12881309
break;
12891310
case SSL_ERROR_SYSCALL:
12901311
DBG( 3, "SSL_ERROR_SYSCALL: Errno = ", errno );
1291-
if ( errno == EAGAIN || errno == EWOULDBLOCK ) {
1312+
if ( errno == PCM_EAGAIN || errno == PCM_EWOULDBLOCK ) {
12921313
DBG( 3, dbg, "Most likely the set timeout, so aborting..." );
12931314
close();
12941315
Base::setg( nullptr, nullptr, nullptr );
@@ -2128,7 +2149,16 @@ struct URL {
21282149
DBG( 3, "number of characters processed: ", pos );
21292150
} catch ( std::out_of_range& e ) {
21302151
DBG( 3, "out_of_range exception caught in stoull: ", e.what() );
2152+
#ifdef _MSC_VER
2153+
char errbuf[256];
2154+
if (strerror_s(errbuf, sizeof(errbuf), errno) == 0) {
2155+
DBG( 3, "errno: ", errno, ", strerror(errno): ", errbuf );
2156+
} else {
2157+
DBG( 3, "errno: ", errno, ", strerror(errno): (error converting)" );
2158+
}
2159+
#else
21312160
DBG( 3, "errno: ", errno, ", strerror(errno): ", strerror(errno) );
2161+
#endif
21322162
}
21332163
}
21342164
if ( port >= 65536 )
@@ -3173,9 +3203,10 @@ class HTTPConnection : public Work {
31733203
}
31743204

31753205
// Do processing of the request here
3176-
if (*callbackList_[request.method()])
3177-
(*callbackList_[request.method()])( hs_, request, response );
3178-
else {
3206+
auto callback = callbackList_[request.method()];
3207+
if ( callback ) {
3208+
(*callback)( hs_, request, response );
3209+
} else {
31793210
std::string body( "501 Not Implemented." );
31803211
body += " Method \"" + HTTPMethodProperties::getMethodAsString(request.method()) + "\" is not implemented (yet).";
31813212
response.createResponse( TextPlain, body, RC_501_NotImplemented );
@@ -3511,8 +3542,8 @@ void HTTPServer::run() {
35113542
HTTPConnection* connection = nullptr;
35123543
try {
35133544
connection = new HTTPConnection( this, clientSocketFD, clientAddress, callbackList_ );
3514-
} catch ( std::exception& e ) {
3515-
DBG( 3, "Exception caught while creating a HTTPConnection: " );
3545+
} catch ( const std::exception& e ) {
3546+
DBG( 3, "Exception caught while creating a HTTPConnection: ", e.what() );
35163547
deleteAndNullify( connection );
35173548
DBG( 3, "close clientsocketFD" );
35183549
::close( clientSocketFD );
@@ -4101,11 +4132,22 @@ int mainThrows(int argc, char * argv[]) {
41014132
else if ( check_argument_equals( argv[i], {"-p"} ) )
41024133
{
41034134
if ( (++i) < argc ) {
4104-
std::stringstream ss( argv[i] );
41054135
try {
4106-
ss >> port;
4107-
} catch( std::exception& e ) {
4108-
std::cerr << "main: port number is not an unsigned short!\n";
4136+
std::size_t pos = 0;
4137+
unsigned long val = std::stoul( argv[i], &pos );
4138+
if ( pos != std::strlen( argv[i] ) )
4139+
throw std::invalid_argument( "invalid port" );
4140+
if ( val > (std::numeric_limits<unsigned short>::max)() )
4141+
throw std::out_of_range( "port out of range" );
4142+
port = static_cast<unsigned short>( val );
4143+
} catch( const std::invalid_argument& e ) {
4144+
std::cerr << "main: invalid port argument '" << argv[i] << "': " << e.what() << "\n";
4145+
::exit( 2 );
4146+
} catch( const std::out_of_range& e ) {
4147+
std::cerr << "main: port argument out of range '" << argv[i] << "': " << e.what() << "\n";
4148+
::exit( 2 );
4149+
} catch( const std::exception& e ) {
4150+
std::cerr << "main: failed to parse port argument '" << argv[i] << "': " << e.what() << "\n";
41094151
::exit( 2 );
41104152
}
41114153
} else {
@@ -4144,11 +4186,19 @@ int mainThrows(int argc, char * argv[]) {
41444186
else if ( check_argument_equals( argv[i], {"-D", "--debug"} ) )
41454187
{
41464188
if ( (++i) < argc ) {
4147-
std::stringstream ss( argv[i] );
41484189
try {
4149-
ss >> debug_level;
4150-
} catch( std::exception& e ) {
4151-
std::cerr << "main: debug level is not an unsigned short!\n";
4190+
std::size_t pos = 0;
4191+
unsigned long val = std::stoul( argv[i], &pos );
4192+
if ( pos != std::strlen( argv[i] ) )
4193+
throw std::invalid_argument( "invalid debug level" );
4194+
if ( val > (std::numeric_limits<unsigned short>::max)() )
4195+
throw std::out_of_range( "debug level out of range" );
4196+
debug_level = static_cast<unsigned short>( val );
4197+
} catch ( const std::invalid_argument& e ) {
4198+
std::cerr << "main: invalid debug level '" << argv[i] << "': " << e.what() << "\n";
4199+
::exit( 2 );
4200+
} catch ( const std::out_of_range& e ) {
4201+
std::cerr << "main: debug level '" << argv[i] << "' is out of range: " << e.what() << "\n";
41524202
::exit( 2 );
41534203
}
41544204
} else {

0 commit comments

Comments
 (0)