-
Notifications
You must be signed in to change notification settings - Fork 0
Fix build for modern C++17 and Clang 17 #1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Modernize the project to build with contemporary toolchains: - Upgrade C++ standard from C++11 to C++17 - Update CMake minimum version to 3.16 - Fix logger comparator const-correctness for modern libc++ - Resolve namespace/class name ambiguity using inline variables - Use typedef alias to disambiguate out-of-line member definitions The dotenv class name matches its namespace, which causes parsing issues in modern C++. Fixed by using C++17 inline variables for static members and a typedef alias in the implementation.
Add a new CMake option BUILD_BOTH_LIBRARIES that allows building both static and shared libraries in a single build directory. This provides an alternative to the existing approach of building in separate directories with BUILD_SHARED_LIBS=ON/OFF, while maintaining backward compatibility with existing build scripts. Usage: cmake .. -DBUILD_BOTH_LIBRARIES=ON Produces: - libcpp_dotenv_static.a - libcpp_dotenv_shared.so (or .dylib on macOS)
|
@coderabbitai review |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This PR modernizes the cpp-dotenv project to support C++17 and Clang 17+ by updating the C++ standard, CMake configuration, and fixing compatibility issues with modern toolchains.
Changes:
- Upgraded from C++11 to C++17 standard across all CMakeLists.txt files
- Added const qualifier to logger comparator for modern libc++ compatibility
- Introduced C++17 inline variables to resolve namespace/class name collision
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| CMakeLists.txt | Updated minimum CMake version to 3.16 and C++ standard to 17 |
| common/libs/antlr4-cpp-runtime/CMakeLists.txt | Updated CMake version to 3.10 and C++ standard to 17 |
| include/dotenv.h | Added inline static member definitions and changed default parameter from env_filename to literal |
| src/dotenv.cpp | Added typedef workaround and fully qualified names to resolve namespace/class collision |
| src/common/logger.h | Added const qualifier to comparator operator() declaration |
| src/common/logger.cpp | Added const qualifier to comparator operator() implementation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| inline const std::string dotenv::env_filename = ".env"; | ||
| inline dotenv dotenv::_instance; |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inline variable definitions on lines 47-48 conflict with the existing static member declarations on lines 38-39 (which are still in the class). When using C++17 inline variables, the static member declarations inside the class should be removed. Currently, this creates duplicate definitions that violate the One Definition Rule and will cause linker errors.
src/dotenv.cpp
Outdated
|
|
||
|
|
||
| const dotenv::dotenv::value_type dotenv::dotenv::operator[](const key_type& k) const | ||
| auto ::dotenv::dotenv::operator[](const key_type& k) const -> const string |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The return type declaration uses a trailing return type syntax auto ... -> const string, but this is inconsistent with the header declaration which uses const value_type. While value_type is a typedef for std::string, using the auto with trailing return type is unnecessary here and creates a mismatch in style. Consider using the same return type as declared in the header: const value_type or const string.
| auto ::dotenv::dotenv::operator[](const key_type& k) const -> const string | |
| const value_type ::dotenv::dotenv::operator[](const key_type& k) const |
src/dotenv.cpp
Outdated
| dotenv::dotenv& dotenv::dotenv::load_dotenv(const string& dotenv_path, const bool overwrite, const bool interpolate) | ||
| // Typedef to work around the fact that class name matches namespace name | ||
| // This is a known C++ limitation when defining out-of-line members | ||
| typedef class ::dotenv::dotenv Dotenv_Type; |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The typedef Dotenv_Type uses an unconventional naming style (Pascal_Snake_Case) that doesn't match typical C++ naming conventions. Consider using either DotenvType (PascalCase) or dotenv_type (snake_case) for better consistency with common C++ style guides.
| @@ -1,7 +1,7 @@ | |||
| cmake_minimum_required(VERSION 3.0.2) | |||
| cmake_minimum_required(VERSION 3.10) | |||
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CMake minimum version is set to 3.10, but the main CMakeLists.txt now requires 3.16. This creates an inconsistency where a subdirectory has a lower minimum version than the parent project. Consider updating this to match the parent project's requirement of 3.16 for consistency, or ensure this subdirectory can genuinely build with just 3.10.
| cmake_minimum_required(VERSION 3.10) | |
| cmake_minimum_required(VERSION 3.16) |
- Add validation to prevent BUILD_BOTH_LIBRARIES and BUILD_SHARED_LIBS from being used together (with warning message) - Improve documentation for the cpp_dotenv alias, explaining that it points to cpp_dotenv_static when BUILD_BOTH_LIBRARIES is ON
- Add OUTPUT_NAME property so libraries are named correctly: * libcpp_dotenv.a (static) instead of libcpp_dotenv_static.a * libcpp_dotenv.so/.dylib (shared) instead of libcpp_dotenv_shared.so - Enable CMAKE_POSITION_INDEPENDENT_CODE when building shared libraries to ensure static dependencies are compiled with -fPIC - Document BUILD_BOTH_LIBRARIES option in README.md with usage examples The OUTPUT_NAME property sets only the base name; CMake automatically adds the correct platform-specific extension (.so on Linux, .dylib on macOS).
- Fix README CMake version mismatch: update from >=3.10 to >=3.16 to match CMakeLists.txt requirement - Fix Windows MSVC import library conflict: when BUILD_BOTH_LIBRARIES is ON, both static and shared libraries would produce cpp_dotenv.lib on Windows. Add IMPORT_SUFFIX "_shared.lib" to the shared library's import library to avoid this conflict on Windows/MSVC.
Add BUILD_BOTH_LIBRARIES option for TAP tests
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. 📝 WalkthroughWalkthroughUpgrades CMake (≥3.16) and C++ standard (→17), adds BUILD_BOTH_LIBRARIES to produce static and shared targets (with aliasing and PIC/validation), and applies C++17 inline/static-member changes plus const-correctness and minor implementation adjustments. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 6 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
CMakeLists.txt
Outdated
| if ("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") | ||
| set(CPP_DOTENV_COMPILE_OPTIONS -g -Wall -O0) | ||
| else() | ||
| set(CPP_DOTENV_COMPILE_OPTIONS -O3) |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The compile options are hardcoded for GCC/Clang-style flags (-g, -Wall, -O0, -O3). These flags won't work correctly with MSVC on Windows. Since the code includes MSVC-specific logic for import libraries (lines 77-79), the build system should also handle MSVC-specific compiler flags.
Consider using generator expressions or checking the compiler type to set appropriate flags for each compiler. For example:
- MSVC uses
/W4instead of-Wall,/Odinstead of-O0,/O2instead of-O3 - Or use CMake's built-in
CMAKE_CXX_FLAGS_DEBUGandCMAKE_CXX_FLAGS_RELEASEwhich handle compiler differences automatically
| if ("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") | |
| set(CPP_DOTENV_COMPILE_OPTIONS -g -Wall -O0) | |
| else() | |
| set(CPP_DOTENV_COMPILE_OPTIONS -O3) | |
| if (MSVC) | |
| # MSVC-specific flags | |
| if ("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") | |
| set(CPP_DOTENV_COMPILE_OPTIONS /W4 /Od) | |
| else() | |
| set(CPP_DOTENV_COMPILE_OPTIONS /W4 /O2) | |
| endif() | |
| else() | |
| # GCC/Clang and other compilers using Unix-style flags | |
| if ("${CMAKE_BUILD_TYPE}" STREQUAL "DEBUG") | |
| set(CPP_DOTENV_COMPILE_OPTIONS -g -Wall -O0) | |
| else() | |
| set(CPP_DOTENV_COMPILE_OPTIONS -O3) | |
| endif() |
CMakeLists.txt
Outdated
| # Set output name to libcpp_dotenv.so/.dylib (instead of libcpp_dotenv_shared.so) | ||
| set_target_properties(cpp_dotenv_shared PROPERTIES OUTPUT_NAME cpp_dotenv) | ||
| # On Windows (MSVC), the import library would conflict with the static library. | ||
| # Add a suffix to the import library name to avoid this conflict. | ||
| if(MSVC) | ||
| set_target_properties(cpp_dotenv_shared PROPERTIES IMPORT_SUFFIX "_shared.lib") |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When BUILD_BOTH_LIBRARIES is ON, both cpp_dotenv_static and cpp_dotenv_shared are configured to use the same OUTPUT_NAME "cpp_dotenv" (lines 63 and 74). This means both libraries will try to create files with the same base name in the same directory:
- Static library:
libcpp_dotenv.a(orcpp_dotenv.libon Windows) - Shared library:
libcpp_dotenv.so/.dylib(orcpp_dotenv.dllon Windows)
While this works on Unix-like systems where static and shared libraries have different extensions (.a vs .so/.dylib), on Windows with MSVC, both the static library and the import library for the shared library would have the .lib extension, causing a file conflict. The IMPORT_SUFFIX workaround on lines 77-79 only addresses the import library suffix but doesn't prevent the base name conflict for the actual static library file.
Consider using different output names when BUILD_BOTH_LIBRARIES is enabled, or documenting that this configuration is not fully supported on Windows with MSVC.
| # Set output name to libcpp_dotenv.so/.dylib (instead of libcpp_dotenv_shared.so) | |
| set_target_properties(cpp_dotenv_shared PROPERTIES OUTPUT_NAME cpp_dotenv) | |
| # On Windows (MSVC), the import library would conflict with the static library. | |
| # Add a suffix to the import library name to avoid this conflict. | |
| if(MSVC) | |
| set_target_properties(cpp_dotenv_shared PROPERTIES IMPORT_SUFFIX "_shared.lib") | |
| # Set output name. On non-MSVC platforms, both static and shared libraries use | |
| # the same base name "cpp_dotenv". On MSVC, give the shared library a distinct | |
| # base name to avoid conflicts between the static library (.lib) and the | |
| # import library for the shared library (also .lib). | |
| if(MSVC) | |
| set_target_properties(cpp_dotenv_shared PROPERTIES OUTPUT_NAME cpp_dotenv_shared) | |
| else() | |
| set_target_properties(cpp_dotenv_shared PROPERTIES OUTPUT_NAME cpp_dotenv) |
| public: | ||
|
|
||
| dotenv& load_dotenv(const std::string& dotenv_path = env_filename, | ||
| dotenv& load_dotenv(const std::string& dotenv_path = ".env", |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default parameter has been changed from env_filename (a static member variable) to the string literal ".env". While this resolves the compilation issue and the values are equivalent, this change creates code duplication - the same string literal ".env" now appears in two places (here and in the inline variable definition on line 47).
If the default filename ever needs to change, it would need to be updated in both locations. Consider whether the static member env_filename is still necessary, or if it should be removed entirely in favor of using the literal directly in both places. Alternatively, the function signature could reference the static member once the inline variable syntax is corrected.
| dotenv& load_dotenv(const std::string& dotenv_path = ".env", | |
| dotenv& load_dotenv(const std::string& dotenv_path = env_filename, |
| inline const std::string dotenv::env_filename = ".env"; | ||
| inline dotenv dotenv::_instance; |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The inline variable declarations for static members appear outside the class definition. According to C++17 rules, when using the inline keyword for static data members, they should still be declared (not defined) inside the class. The definitions with inline should either be inside the class body or be marked as inline at their declaration point within the class.
The current approach declares these members as non-inline static members within the class (lines 38-39) but then attempts to define them as inline outside the class. This creates a mismatch. For proper C++17 inline variables, the declaration inside the class should include the inline keyword and the initialization, like:
static inline const std::string env_filename = ".env";
static inline dotenv _instance;Then these lines 47-48 would not be needed at all.
src/dotenv.cpp
Outdated
| dotenv::dotenv& dotenv::dotenv::load_dotenv(const string& dotenv_path, const bool overwrite, const bool interpolate) | ||
| // Typedef to work around the fact that class name matches namespace name | ||
| // This is a known C++ limitation when defining out-of-line members | ||
| typedef class ::dotenv::dotenv Dotenv_Type; |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The typedef uses inconsistent naming convention. In C++ projects, type aliases are typically named in PascalCase or snake_case matching the project's style. The name Dotenv_Type mixes conventions (capitalized first word with underscore separator).
Consider using a more conventional name like DotenvClass or dotenv_class to match the project's naming patterns. Additionally, since this is just a workaround for the namespace/class name conflict, a clearer name like DotenvImpl might better communicate its purpose.
src/dotenv.cpp
Outdated
| dotenv::dotenv dotenv::dotenv::_instance; | ||
|
|
||
| dotenv::dotenv& dotenv::env = dotenv::instance(); | ||
| // Static members are now inline in the header (C++17) |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
With the C++17 inline variable approach used in the header file (lines 47-48 of dotenv.h), the static member variables env_filename and _instance are already defined in the header. The comment on line 46 states "Static members are now inline in the header (C++17)", which means the definitions of these members should not exist in the .cpp file at all.
However, this line defines env as a separate extern variable (not a static member). This is correct and should remain, but the comment is misleading as it suggests static members are being moved to inline definitions, when in fact only env (which is not a static member) is being defined here.
| // Static members are now inline in the header (C++17) | |
| // Define the global 'env' instance; static members are inline in the header (C++17) |
dotenv.h: - Clarify inline variable syntax with explanatory comment - Add comment explaining why _instance cannot be inline inside class dotenv.cpp: - Rename Dotenv_Type to DotenvClass for better C++ conventions - Fix operator[] to use typedef alias consistently (avoid parsing issues) - Improve comment clarity for the global env variable definition CMakeLists.txt: - Add MSVC support for compiler flags (/W4 /Od /O2) - Improve Windows library naming: use distinct OUTPUT_NAME on MSVC to avoid conflicts between static and shared library .lib files common/libs/antlr4-cpp-runtime/CMakeLists.txt: - Update cmake_minimum_required from 3.10 to 3.16 for consistency
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
Copilot reviewed 7 out of 7 changed files in this pull request and generated 2 comments.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| private: | ||
|
|
||
| // Declare static members (definitions are inline after the class) |
Copilot
AI
Jan 21, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The static member env_filename is declared and defined (line 47) but appears to be unused after changing the default parameter from env_filename to ".env" on line 18. Consider removing this static member unless it's being kept for backward compatibility with external code that may reference dotenv::dotenv::env_filename. If it's intentionally kept for backward compatibility, add a comment explaining this.
| // Declare static members (definitions are inline after the class) | |
| // Declare static members (definitions are inline after the class) | |
| // NOTE: env_filename is kept for backward compatibility; external code | |
| // may reference dotenv::dotenv::env_filename directly. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.
Address remaining Copilot feedback: env_filename is kept for backward compatibility as external code may reference dotenv::dotenv::env_filename directly. Add explanatory comment to document this.
Summary
This PR modernizes the cpp-dotenv project to build with contemporary C++ toolchains (C++17, Clang 17+, modern CMake).
Changes
logger::position::less::operator()for modern libc++ compatibilitydotenv) using C++17 inline variablesTechnical Details
The
dotenvclass name matches its namespace, which causes C++ parser ambiguity when defining out-of-line members. This is fixed by:inlinevariables for static member definitionsTest Plan
libcpp_dotenv.abuilds without errorsSummary by CodeRabbit
New Features
Documentation
Bug Fixes
Refactor
✏️ Tip: You can customize this high-level summary in your review settings.