Skip to content

Commit e09a729

Browse files
committed
Add support C++20 modules
1 parent 8541e67 commit e09a729

File tree

8 files changed

+2616
-1
lines changed

8 files changed

+2616
-1
lines changed

.github/workflows/cpp20-modules.yml

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
name: C++20
2+
3+
on:
4+
push:
5+
branches: [master]
6+
pull_request:
7+
8+
permissions:
9+
contents: read
10+
11+
jobs:
12+
modules:
13+
runs-on: ubuntu-24.04
14+
steps:
15+
- uses: actions/checkout@v4
16+
17+
- name: Install cmake && ninja-build
18+
run: |
19+
sudo apt-get install -y cmake ninja-build clang
20+
cmake --version
21+
ninja --version
22+
clang++ --version
23+
24+
- name: Install dependencies
25+
run: |
26+
# Installing packages might fail as the github image becomes outdated
27+
sudo apt update
28+
# These aren't available or don't work well in vcpkg
29+
sudo apt-get install -y libjsoncpp-dev uuid-dev libssl-dev zlib1g-dev libsqlite3-dev
30+
sudo apt-get install -y ninja-build libbrotli-dev
31+
32+
- name: Build Modules
33+
run: |
34+
cmake -B build -DCMAKE_CXX_STANDARD=20 -GNinja -DDROGON_BUILD_MODULES=ON -DDROGON_BUILD_MODULES_EXAMPLE=ON
35+
ninja -j 9

CMakeLists.txt

Lines changed: 100 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,97 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
5454
endif ()
5555

5656
add_library(${PROJECT_NAME})
57+
58+
option(DROGON_BUILD_MODULES "Build drogon library in C++20 Modules form" OFF)
59+
option(DROGON_BUILD_MODULES_EXAMPLE "Build drogon modules example" OFF)
60+
option(HAS_STD_MODULE "has c++ std module" OFF)
61+
62+
if(${DROGON_BUILD_MODULES})
63+
message("-- use c++20 module")
64+
65+
if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28)
66+
cmake_policy(SET CMP0155 NEW)
67+
elseif(CMAKE_VERSION VERSION_GREATER_EQUAL 3.27)
68+
set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
69+
elseif(CMAKE_VERSION VERSION_GREATER_EQUAL 3.26)
70+
set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "2182bf5c-ef0d-489a-91da-49dbc3090d2a")
71+
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
72+
set(CMake_TEST_CXXModules_UUID "a246741c-d067-4019-a8fb-3d16b0c9d1d3")
73+
set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
74+
string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
75+
"${CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS}"
76+
" -format=p1689"
77+
" --"
78+
" <CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS>"
79+
" -x c++ <SOURCE> -c -o <OBJECT>"
80+
" -MT <DYNDEP_FILE>"
81+
" -MD -MF <DEP_FILE>"
82+
" > <DYNDEP_FILE>")
83+
set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "clang")
84+
set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>")
85+
86+
set(CMAKE_CXX_EXTENSIONS OFF)
87+
endif()
88+
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
89+
set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
90+
endif()
91+
endif()
92+
93+
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
94+
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "16.0.0")
95+
string(CONCAT CMAKE_CXX_SCANDEP_SOURCE
96+
"\"${CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS}\""
97+
" -format=p1689"
98+
" --"
99+
" <CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS>"
100+
" -x c++ <SOURCE> -c -o <OBJECT>"
101+
" -MT <DYNDEP_FILE>"
102+
" -MD -MF <DEP_FILE>"
103+
" > <DYNDEP_FILE>.tmp"
104+
" && mv <DYNDEP_FILE>.tmp <DYNDEP_FILE>")
105+
set(CMAKE_CXX_MODULE_MAP_FORMAT "clang")
106+
set(CMAKE_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>")
107+
set(CMAKE_CXX_MODULE_BMI_ONLY_FLAG "--precompile")
108+
endif()
109+
endif()
110+
111+
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
112+
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "16.0.0")
113+
string(CONCAT CMAKE_CXX_SCANDEP_SOURCE
114+
"\"${CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS}\""
115+
" -format=p1689"
116+
" --"
117+
" <CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS>"
118+
" -x c++ <SOURCE> -c -o <OBJECT>"
119+
" -MT <DYNDEP_FILE>"
120+
" -MD -MF <DEP_FILE>"
121+
" > <DYNDEP_FILE>.tmp"
122+
" && mv <DYNDEP_FILE>.tmp <DYNDEP_FILE>")
123+
set(CMAKE_CXX_MODULE_MAP_FORMAT "clang")
124+
set(CMAKE_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>")
125+
set(CMAKE_CXX_MODULE_BMI_ONLY_FLAG "--precompile")
126+
endif()
127+
endif()
128+
129+
set(CMAKE_CXX_STANDARD 20)
130+
set(CMAKE_CXX_STANDARD_REQUIRED ON)
131+
132+
set(MODULES_SRCS "")
133+
list(APPEND MODULES_SRCS ${PROJECT_SOURCE_DIR}/modules/drogon.cppm)
134+
if (NOT ${HAS_STD_MODULE})
135+
list(APPEND MODULES_SRCS ${PROJECT_SOURCE_DIR}/modules/std.mock.cppm)
136+
endif()
137+
138+
message("${MODULES_SRCS}")
139+
140+
target_sources(${PROJECT_NAME} PUBLIC FILE_SET CXX_MODULES FILES ${MODULES_SRCS})
141+
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)
142+
# build modules example
143+
if(${DROGON_BUILD_MODULES_EXAMPLE})
144+
add_subdirectory(examples/cpp20_modules_example)
145+
endif()
146+
endif()
147+
57148
if (BUILD_SHARED_LIBS)
58149
find_package(Threads)
59150
# set(BUILD_EXAMPLES FALSE)
@@ -678,12 +769,20 @@ if (BUILD_TESTING)
678769
endif (BUILD_TESTING)
679770

680771
# Installation
681-
772+
if(${DROGON_BUILD_MODULES})
773+
install(TARGETS ${PROJECT_NAME}
774+
EXPORT DrogonTargets
775+
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin
776+
ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib
777+
LIBRARY FILE_SET CXX_MODULES DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib)
778+
else()
682779
install(TARGETS ${PROJECT_NAME}
683780
EXPORT DrogonTargets
684781
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin
685782
ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib
686783
LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib)
784+
endif()
785+
687786

688787
install(FILES ${DROGON_HEADERS} DESTINATION ${INSTALL_INCLUDE_DIR}/drogon)
689788

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
cmake_minimum_required(VERSION 3.28)
2+
3+
project(modules_example)
4+
5+
set(CMAKE_CXX_STANDARD 20)
6+
7+
# Default to C++ extensions being off. Clang's modules support have trouble
8+
# with extensions right now.
9+
set(CMAKE_CXX_EXTENSIONS OFF)
10+
11+
find_package(jsoncpp REQUIRED)
12+
find_package(OpenSSL REQUIRED)
13+
# set(CMAKE_PREFIX_PATH /root/github/drogon/build/nqf/lib/cmake/Drogon;/root/github/drogon/build/nqf/lib/cmake/Trantor)
14+
# find_package(Drogon REQUIRED)
15+
16+
add_executable(module_client module_client.cpp)
17+
target_link_libraries(module_client drogon OpenSSL::SSL OpenSSL::Crypto uuid pthread dl)
18+
19+
add_executable(module_std module_std.cpp)
20+
target_link_libraries(module_std PUBLIC drogon OpenSSL::SSL OpenSSL::Crypto uuid pthread dl)
21+
22+
add_executable(module_server module_server.cpp)
23+
target_link_libraries(module_server PUBLIC drogon OpenSSL::SSL OpenSSL::Crypto uuid pthread dl)
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import drogon;
2+
import std;
3+
4+
#include <trantor/utils/Logger.h>
5+
6+
#ifdef __linux__
7+
#include <sys/socket.h>
8+
#include <netinet/tcp.h>
9+
#endif
10+
11+
using namespace drogon;
12+
13+
int nth_resp = 0;
14+
15+
int main()
16+
{
17+
trantor::Logger::setLogLevel(trantor::Logger::kTrace);
18+
{
19+
auto client = HttpClient::newHttpClient("http://www.baidu.com");
20+
client->setSockOptCallback([](int fd) {
21+
std::cout << "setSockOptCallback:" << fd << std::endl;
22+
#ifdef __linux__
23+
int optval = 10;
24+
::setsockopt(fd,
25+
SOL_TCP,
26+
TCP_KEEPCNT,
27+
&optval,
28+
static_cast<socklen_t>(sizeof optval));
29+
::setsockopt(fd,
30+
SOL_TCP,
31+
TCP_KEEPIDLE,
32+
&optval,
33+
static_cast<socklen_t>(sizeof optval));
34+
::setsockopt(fd,
35+
SOL_TCP,
36+
TCP_KEEPINTVL,
37+
&optval,
38+
static_cast<socklen_t>(sizeof optval));
39+
#endif
40+
});
41+
42+
auto req = HttpRequest::newHttpRequest();
43+
req->setMethod(drogon::Get);
44+
req->setPath("/s");
45+
req->setParameter("wd", "wx");
46+
req->setParameter("oq", "wx");
47+
48+
for (int i = 0; i < 10; ++i)
49+
{
50+
client->sendRequest(
51+
req, [](ReqResult result, const HttpResponsePtr &response) {
52+
if (result != ReqResult::Ok)
53+
{
54+
std::cout
55+
<< "error while sending request to server! result: "
56+
<< result << std::endl;
57+
return;
58+
}
59+
60+
std::cout << "receive response!" << std::endl;
61+
// auto headers=response.
62+
++nth_resp;
63+
std::cout << response->getBody() << std::endl;
64+
auto cookies = response->cookies();
65+
for (auto const &cookie : cookies)
66+
{
67+
std::cout << cookie.first << "="
68+
<< cookie.second.value()
69+
<< ":domain=" << cookie.second.domain()
70+
<< std::endl;
71+
}
72+
std::cout << "count=" << nth_resp << std::endl;
73+
});
74+
}
75+
std::cout << "requestsBufferSize:" << client->requestsBufferSize()
76+
<< std::endl;
77+
}
78+
79+
app().run();
80+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import drogon;
2+
import std;
3+
4+
#include <trantor/utils/Logger.h>
5+
6+
using namespace drogon;
7+
8+
int main()
9+
{
10+
trantor::Logger::setLogLevel(trantor::Logger::kTrace);
11+
app().registerHandler(
12+
"/",
13+
[](const HttpRequestPtr &request,
14+
std::function<void(const HttpResponsePtr &)> &&callback) {
15+
LOG_INFO << "connected:"
16+
<< (request->connected() ? "true" : "false");
17+
auto resp = HttpResponse::newHttpResponse();
18+
resp->setBody("Hello, World!");
19+
callback(resp);
20+
},
21+
{Get});
22+
23+
LOG_INFO << "Server running on 127.0.0.1:8848";
24+
app().addListener("127.0.0.1", 8848).run();
25+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import std;
2+
3+
int main()
4+
{
5+
std::cout << "hello world" << std::endl;
6+
7+
std::vector<std::string> vec;
8+
vec.emplace_back("hello world");
9+
std::cout << vec.size() << std::endl;
10+
11+
std::unordered_map<int, int> mp;
12+
mp.emplace(1, 2);
13+
std::cout << mp.size() << std::endl;
14+
15+
auto it =
16+
std::ranges::find_if(vec, [](auto &&p) { return p == "hello world"; });
17+
if (it != vec.end())
18+
{
19+
std::cout << "found" << std::endl;
20+
}
21+
22+
std::unordered_set<int> s;
23+
s.insert(100);
24+
std::cout << s.size() << std::endl;
25+
26+
std::shared_ptr<std::string> ptr_1;
27+
std::unique_ptr<std::string> ptr_2;
28+
29+
return 0;
30+
}

modules/drogon.cppm

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
module;
2+
3+
#include <drogon/drogon.h>
4+
#include <drogon/HttpSimpleController.h>
5+
#include <drogon/WebSocketClient.h>
6+
#include <drogon/WebSocketController.h>
7+
#include <drogon/PubSubService.h>
8+
#include <drogon/orm/Mapper.h>
9+
10+
export module drogon;
11+
12+
export namespace drogon
13+
{
14+
15+
using drogon::HttpClient;
16+
using drogon::HttpRequest;
17+
using drogon::HttpRequestPtr;
18+
using drogon::HttpResponse;
19+
using drogon::HttpResponsePtr;
20+
using drogon::MultiPartParser;
21+
using drogon::RequestStreamPtr;
22+
using drogon::ResponseStream;
23+
using drogon::ResponseStreamPtr;
24+
25+
using drogon::ContentType;
26+
using drogon::HttpMethod;
27+
using drogon::HttpStatusCode;
28+
using drogon::ReqResult;
29+
using drogon::to_string;
30+
using drogon::to_string_view;
31+
using drogon::operator<<;
32+
using drogon::Version;
33+
using drogon::WebSocketMessageType;
34+
35+
using drogon::HttpSimpleController;
36+
using drogon::HttpViewData;
37+
38+
using drogon::PubSubService;
39+
using drogon::WebSocketClient;
40+
using drogon::WebSocketClientPtr;
41+
using drogon::WebSocketConnectionPtr;
42+
using drogon::WebSocketController;
43+
44+
using drogon::app;
45+
46+
using drogon::nosql::RedisClient;
47+
using drogon::nosql::RedisTransactionPtr;
48+
49+
using drogon::orm::ConstRowIterator;
50+
using drogon::orm::DbClient;
51+
using drogon::orm::DrogonDbException;
52+
using drogon::orm::Field;
53+
using drogon::orm::Mapper;
54+
using drogon::orm::Result;
55+
56+
} // namespace drogon

0 commit comments

Comments
 (0)