Skip to content

Commit

Permalink
Add support C++20 modules
Browse files Browse the repository at this point in the history
  • Loading branch information
fantasy-peak committed Nov 4, 2024
1 parent 8541e67 commit 8bc2a37
Show file tree
Hide file tree
Showing 8 changed files with 2,622 additions and 1 deletion.
41 changes: 41 additions & 0 deletions .github/workflows/cpp20-modules.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: C++20

on:
push:
branches: [master]
pull_request:

permissions:
contents: read

jobs:
modules:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4

- name: Checkout Drogon source code
uses: actions/checkout@v4
with:
submodules: true
fetch-depth: 0

- name: Install cmake && ninja-build
run: |
sudo apt-get install -y cmake ninja-build clang
cmake --version
ninja --version
clang++ --version
- name: Install dependencies
run: |
# Installing packages might fail as the github image becomes outdated
sudo apt update
# These aren't available or don't work well in vcpkg
sudo apt-get install -y libjsoncpp-dev uuid-dev libssl-dev zlib1g-dev libsqlite3-dev
sudo apt-get install -y ninja-build libbrotli-dev
- name: Build Modules
run: |
cmake -B build -DCMAKE_CXX_STANDARD=20 -GNinja -DDROGON_BUILD_MODULES=ON -DDROGON_BUILD_MODULES_EXAMPLE=ON
ninja -j 9
101 changes: 100 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,97 @@ if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
endif ()

add_library(${PROJECT_NAME})

option(DROGON_BUILD_MODULES "Build drogon library in C++20 Modules form" OFF)
option(DROGON_BUILD_MODULES_EXAMPLE "Build drogon modules example" OFF)
option(HAS_STD_MODULE "has c++ std module" OFF)

if(${DROGON_BUILD_MODULES})
message("-- use c++20 module")

if(CMAKE_VERSION VERSION_GREATER_EQUAL 3.28)
cmake_policy(SET CMP0155 NEW)
elseif(CMAKE_VERSION VERSION_GREATER_EQUAL 3.27)
set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "aa1f7df0-828a-4fcd-9afc-2dc80491aca7")
elseif(CMAKE_VERSION VERSION_GREATER_EQUAL 3.26)
set(CMAKE_EXPERIMENTAL_CXX_MODULE_CMAKE_API "2182bf5c-ef0d-489a-91da-49dbc3090d2a")
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
set(CMake_TEST_CXXModules_UUID "a246741c-d067-4019-a8fb-3d16b0c9d1d3")
set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
string(CONCAT CMAKE_EXPERIMENTAL_CXX_SCANDEP_SOURCE
"${CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS}"
" -format=p1689"
" --"
" <CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS>"
" -x c++ <SOURCE> -c -o <OBJECT>"
" -MT <DYNDEP_FILE>"
" -MD -MF <DEP_FILE>"
" > <DYNDEP_FILE>")
set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FORMAT "clang")
set(CMAKE_EXPERIMENTAL_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>")

set(CMAKE_CXX_EXTENSIONS OFF)
endif()
if (CMAKE_CXX_COMPILER_ID MATCHES "MSVC")
set(CMAKE_EXPERIMENTAL_CXX_MODULE_DYNDEP 1)
endif()
endif()

if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "16.0.0")
string(CONCAT CMAKE_CXX_SCANDEP_SOURCE
"\"${CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS}\""
" -format=p1689"
" --"
" <CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS>"
" -x c++ <SOURCE> -c -o <OBJECT>"
" -MT <DYNDEP_FILE>"
" -MD -MF <DEP_FILE>"
" > <DYNDEP_FILE>.tmp"
" && mv <DYNDEP_FILE>.tmp <DYNDEP_FILE>")
set(CMAKE_CXX_MODULE_MAP_FORMAT "clang")
set(CMAKE_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>")
set(CMAKE_CXX_MODULE_BMI_ONLY_FLAG "--precompile")
endif()
endif()

if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS "16.0.0")
string(CONCAT CMAKE_CXX_SCANDEP_SOURCE
"\"${CMAKE_CXX_COMPILER_CLANG_SCAN_DEPS}\""
" -format=p1689"
" --"
" <CMAKE_CXX_COMPILER> <DEFINES> <INCLUDES> <FLAGS>"
" -x c++ <SOURCE> -c -o <OBJECT>"
" -MT <DYNDEP_FILE>"
" -MD -MF <DEP_FILE>"
" > <DYNDEP_FILE>.tmp"
" && mv <DYNDEP_FILE>.tmp <DYNDEP_FILE>")
set(CMAKE_CXX_MODULE_MAP_FORMAT "clang")
set(CMAKE_CXX_MODULE_MAP_FLAG "@<MODULE_MAP_FILE>")
set(CMAKE_CXX_MODULE_BMI_ONLY_FLAG "--precompile")
endif()
endif()

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

set(MODULES_SRCS "")
list(APPEND MODULES_SRCS ${PROJECT_SOURCE_DIR}/modules/drogon.cppm)
if (NOT ${HAS_STD_MODULE})
list(APPEND MODULES_SRCS ${PROJECT_SOURCE_DIR}/modules/std.mock.cppm)
endif()

message("${MODULES_SRCS}")

target_sources(${PROJECT_NAME} PUBLIC FILE_SET CXX_MODULES FILES ${MODULES_SRCS})
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)
# build modules example
if(${DROGON_BUILD_MODULES_EXAMPLE})
add_subdirectory(examples/cpp20_modules_example)
endif()
endif()

if (BUILD_SHARED_LIBS)
find_package(Threads)
# set(BUILD_EXAMPLES FALSE)
Expand Down Expand Up @@ -678,12 +769,20 @@ if (BUILD_TESTING)
endif (BUILD_TESTING)

# Installation

if(${DROGON_BUILD_MODULES})
install(TARGETS ${PROJECT_NAME}
EXPORT DrogonTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin
ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib
LIBRARY FILE_SET CXX_MODULES DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib)
else()
install(TARGETS ${PROJECT_NAME}
EXPORT DrogonTargets
RUNTIME DESTINATION "${INSTALL_BIN_DIR}" COMPONENT bin
ARCHIVE DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib
LIBRARY DESTINATION "${INSTALL_LIB_DIR}" COMPONENT lib)
endif()


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

Expand Down
23 changes: 23 additions & 0 deletions examples/cpp20_modules_example/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 3.28)

project(modules_example)

set(CMAKE_CXX_STANDARD 20)

# Default to C++ extensions being off. Clang's modules support have trouble
# with extensions right now.
set(CMAKE_CXX_EXTENSIONS OFF)

find_package(jsoncpp REQUIRED)
find_package(OpenSSL REQUIRED)
# set(CMAKE_PREFIX_PATH /root/github/drogon/build/nqf/lib/cmake/Drogon;/root/github/drogon/build/nqf/lib/cmake/Trantor)
# find_package(Drogon REQUIRED)

add_executable(module_client module_client.cpp)
target_link_libraries(module_client drogon OpenSSL::SSL OpenSSL::Crypto uuid pthread dl)

add_executable(module_std module_std.cpp)
target_link_libraries(module_std PUBLIC drogon OpenSSL::SSL OpenSSL::Crypto uuid pthread dl)

add_executable(module_server module_server.cpp)
target_link_libraries(module_server PUBLIC drogon OpenSSL::SSL OpenSSL::Crypto uuid pthread dl)
80 changes: 80 additions & 0 deletions examples/cpp20_modules_example/module_client.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import drogon;
import std;

#include <trantor/utils/Logger.h>

#ifdef __linux__
#include <sys/socket.h>
#include <netinet/tcp.h>
#endif

using namespace drogon;

int nth_resp = 0;

int main()
{
trantor::Logger::setLogLevel(trantor::Logger::kTrace);
{
auto client = HttpClient::newHttpClient("http://www.baidu.com");
client->setSockOptCallback([](int fd) {
std::cout << "setSockOptCallback:" << fd << std::endl;
#ifdef __linux__
int optval = 10;
::setsockopt(fd,
SOL_TCP,
TCP_KEEPCNT,
&optval,
static_cast<socklen_t>(sizeof optval));
::setsockopt(fd,
SOL_TCP,
TCP_KEEPIDLE,
&optval,
static_cast<socklen_t>(sizeof optval));
::setsockopt(fd,
SOL_TCP,
TCP_KEEPINTVL,
&optval,
static_cast<socklen_t>(sizeof optval));
#endif
});

auto req = HttpRequest::newHttpRequest();
req->setMethod(drogon::Get);
req->setPath("/s");
req->setParameter("wd", "wx");
req->setParameter("oq", "wx");

for (int i = 0; i < 10; ++i)
{
client->sendRequest(
req, [](ReqResult result, const HttpResponsePtr &response) {
if (result != ReqResult::Ok)
{
std::cout
<< "error while sending request to server! result: "
<< result << std::endl;
return;
}

std::cout << "receive response!" << std::endl;
// auto headers=response.
++nth_resp;
std::cout << response->getBody() << std::endl;
auto cookies = response->cookies();
for (auto const &cookie : cookies)
{
std::cout << cookie.first << "="
<< cookie.second.value()
<< ":domain=" << cookie.second.domain()
<< std::endl;
}
std::cout << "count=" << nth_resp << std::endl;
});
}
std::cout << "requestsBufferSize:" << client->requestsBufferSize()
<< std::endl;
}

app().run();
}
25 changes: 25 additions & 0 deletions examples/cpp20_modules_example/module_server.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import drogon;
import std;

#include <trantor/utils/Logger.h>

using namespace drogon;

int main()
{
trantor::Logger::setLogLevel(trantor::Logger::kTrace);
app().registerHandler(
"/",
[](const HttpRequestPtr &request,
std::function<void(const HttpResponsePtr &)> &&callback) {
LOG_INFO << "connected:"
<< (request->connected() ? "true" : "false");
auto resp = HttpResponse::newHttpResponse();
resp->setBody("Hello, World!");
callback(resp);
},
{Get});

LOG_INFO << "Server running on 127.0.0.1:8848";
app().addListener("127.0.0.1", 8848).run();
}
30 changes: 30 additions & 0 deletions examples/cpp20_modules_example/module_std.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import std;

int main()
{
std::cout << "hello world" << std::endl;

std::vector<std::string> vec;
vec.emplace_back("hello world");
std::cout << vec.size() << std::endl;

std::unordered_map<int, int> mp;
mp.emplace(1, 2);
std::cout << mp.size() << std::endl;

auto it =
std::ranges::find_if(vec, [](auto &&p) { return p == "hello world"; });
if (it != vec.end())
{
std::cout << "found" << std::endl;
}

std::unordered_set<int> s;
s.insert(100);
std::cout << s.size() << std::endl;

std::shared_ptr<std::string> ptr_1;
std::unique_ptr<std::string> ptr_2;

return 0;
}
56 changes: 56 additions & 0 deletions modules/drogon.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
module;

#include <drogon/drogon.h>
#include <drogon/HttpSimpleController.h>
#include <drogon/WebSocketClient.h>
#include <drogon/WebSocketController.h>
#include <drogon/PubSubService.h>
#include <drogon/orm/Mapper.h>

export module drogon;

export namespace drogon
{

using drogon::HttpClient;
using drogon::HttpRequest;
using drogon::HttpRequestPtr;
using drogon::HttpResponse;
using drogon::HttpResponsePtr;
using drogon::MultiPartParser;
using drogon::RequestStreamPtr;
using drogon::ResponseStream;
using drogon::ResponseStreamPtr;

using drogon::ContentType;
using drogon::HttpMethod;
using drogon::HttpStatusCode;
using drogon::ReqResult;
using drogon::to_string;
using drogon::to_string_view;
using drogon::operator<<;
using drogon::Version;
using drogon::WebSocketMessageType;

using drogon::HttpSimpleController;
using drogon::HttpViewData;

using drogon::PubSubService;
using drogon::WebSocketClient;
using drogon::WebSocketClientPtr;
using drogon::WebSocketConnectionPtr;
using drogon::WebSocketController;

using drogon::app;

using drogon::nosql::RedisClient;
using drogon::nosql::RedisTransactionPtr;

using drogon::orm::ConstRowIterator;
using drogon::orm::DbClient;
using drogon::orm::DrogonDbException;
using drogon::orm::Field;
using drogon::orm::Mapper;
using drogon::orm::Result;

} // namespace drogon
Loading

0 comments on commit 8bc2a37

Please sign in to comment.