Skip to content

Commit 3cc204e

Browse files
committed
Update CHANGELOG, CMake configuration, and examples; refactor MulticastSender class
1 parent 05e2488 commit 3cc204e

File tree

10 files changed

+217
-90
lines changed

10 files changed

+217
-90
lines changed

CHANGELOG

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
#v1.0.2 - [12/05/2025]
2+
- Change MulticastSender from template class to regular class (breaking change)
3+
- Upgrade Google Test to v1.17.0
4+
- Fix MSVC MSB8028 warnings about intermediate directory conflicts
5+
- Set explicit RUNTIME_OUTPUT_DIRECTORY for example executables
6+
- Add MSVC runtime library configuration
7+
- Remove redundant VS_GLOBAL_IntDir properties from custom targets
8+
19
#v1.0.1 - [10/21/2025]
210
- Change include folder structure from include/slick_socket/ to include/slick/
311
- Change namespace from slick_socket to slick::socket

CMakeLists.txt

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,15 @@ set(CMAKE_CXX_STANDARD 20)
44
set(CMAKE_CXX_STANDARD_REQUIRED ON)
55
set(CMAKE_CXX_EXTENSIONS OFF)
66

7-
set(BUILD_VERSION 1.0.1)
8-
project(slick_socket VERSION ${BUILD_VERSION} LANGUAGES C CXX)
7+
project(slick_socket
8+
VERSION 1.0.2
9+
LANGUAGES C CXX
10+
)
11+
12+
# MSVC configuration
13+
if(MSVC)
14+
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>DLL")
15+
endif()
916

1017
if (NOT CMAKE_BUILD_TYPE)
1118
set(CMAKE_BUILD_TYPE Release)
@@ -49,6 +56,7 @@ endif()
4956

5057
if(WIN32)
5158
add_library(slick_socket STATIC src/wepoll.c)
59+
add_library(slick::slick_socket ALIAS slick_socket)
5260
set_target_properties(slick_socket PROPERTIES
5361
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib
5462
)
@@ -58,6 +66,20 @@ if(WIN32)
5866
)
5967
target_compile_definitions(slick_socket PUBLIC _WIN32_WINNT=0x0601)
6068
target_link_libraries(slick_socket PUBLIC ws2_32)
69+
70+
# Shared lib
71+
add_library(slick_socket_shared SHARED src/wepoll.c)
72+
add_library(slick::slick_socket_shared ALIAS slick_socket_shared)
73+
set_target_properties(slick_socket_shared PROPERTIES
74+
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin
75+
)
76+
target_include_directories(slick_socket_shared PUBLIC
77+
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
78+
$<INSTALL_INTERFACE:include>
79+
)
80+
target_compile_definitions(slick_socket_shared PUBLIC _WIN32_WINNT=0x0601)
81+
target_link_libraries(slick_socket_shared PUBLIC ws2_32)
82+
6183
elseif(UNIX)
6284
add_library(slick_socket INTERFACE)
6385
target_include_directories(slick_socket INTERFACE
@@ -96,8 +118,10 @@ if(CMAKE_BUILD_TYPE STREQUAL "Release")
96118

97119
add_custom_command(TARGET dist_slick_socket POST_BUILD
98120
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/dist/lib
121+
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_BINARY_DIR}/dist/bin
99122
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:slick_socket> ${CMAKE_BINARY_DIR}/dist/lib/
100-
COMMENT "Copying slick_socket.lib to dist/lib"
123+
COMMAND ${CMAKE_COMMAND} -E copy $<TARGET_FILE:slick_socket_shared> ${CMAKE_BINARY_DIR}/dist/bin/
124+
COMMENT "Copying slick_socket.lib to dist/lib; Copying slick_socket.dll to dist/bin"
101125
)
102126
else()
103127
add_custom_target(dist_slick_socket ALL
@@ -110,13 +134,13 @@ if(CMAKE_BUILD_TYPE STREQUAL "Release")
110134
if (PROJECT_IS_TOP_LEVEL)
111135
if (WIN32)
112136
add_custom_target(package_slick_socket ALL
113-
COMMAND ${CMAKE_COMMAND} -E tar "cfv" "${CMAKE_BINARY_DIR}/dist/slick_socket_${BUILD_VERSION}.zip" --format=zip "include" "lib"
137+
COMMAND ${CMAKE_COMMAND} -E tar "cfv" "${CMAKE_BINARY_DIR}/dist/slick_socket_${PROJECT_VERSION}.zip" --format=zip "include" "lib" "bin"
114138
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/dist"
115139
COMMENT "Creating zip archive"
116140
)
117141
else()
118142
add_custom_target(package_slick_socket ALL
119-
COMMAND ${CMAKE_COMMAND} -E tar "cfv" "${CMAKE_BINARY_DIR}/dist/slick_socket_${BUILD_VERSION}.zip" --format=zip "include"
143+
COMMAND ${CMAKE_COMMAND} -E tar "cfv" "${CMAKE_BINARY_DIR}/dist/slick_socket_${PROJECT_VERSION}.zip" --format=zip "include"
120144
WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/dist"
121145
COMMENT "Creating zip archive"
122146
)

README.md

Lines changed: 124 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ A header-only C++20 networking library providing cross-platform TCP and UDP mult
1010
## Features
1111

1212
- **Cross-platform**: Windows and Unix/Linux support
13-
- **Header-only**: No separate compilation required
14-
- **Modern C++**: C++20 template-based design with CRTP
13+
- **Header-only**: No separate compilation required (Windows requires linking slick_socket.lib)
14+
- **Modern C++**: C++20 design with CRTP for most components
1515
- **Asynchronous**: Non-blocking socket operations with timeout handling
1616
- **TCP Communication**: Client and server implementations
1717
- **UDP Multicast**: One-to-many communication support
@@ -151,6 +151,126 @@ int main()
151151
}
152152
```
153153
154+
### Creating a TCP Client
155+
156+
```cpp
157+
#include <slick/socket/tcp_client.h>
158+
159+
class MyClient : public slick::socket::TCPClientBase<MyClient>
160+
{
161+
public:
162+
MyClient(const slick::socket::TCPClientConfig& config)
163+
: TCPClientBase("MyClient", config) {}
164+
165+
void onConnected()
166+
{
167+
std::cout << "Connected to server" << std::endl;
168+
}
169+
170+
void onDisconnected()
171+
{
172+
std::cout << "Disconnected from server" << std::endl;
173+
}
174+
175+
void onData(const uint8_t* data, size_t length)
176+
{
177+
std::string received_data((const char*)data, length);
178+
std::cout << "Received: " << received_data << std::endl;
179+
}
180+
};
181+
182+
int main()
183+
{
184+
slick::socket::TCPClientConfig config;
185+
config.server_address = "127.0.0.1";
186+
config.server_port = 5000;
187+
188+
MyClient client(config);
189+
client.connect();
190+
191+
if (client.is_connected())
192+
{
193+
client.send_data("Hello Server!");
194+
// ... process responses
195+
client.disconnect();
196+
}
197+
198+
return 0;
199+
}
200+
```
201+
202+
### Creating a Multicast Sender
203+
204+
```cpp
205+
#include <slick/socket/multicast_sender.h>
206+
207+
int main()
208+
{
209+
slick::socket::MulticastSenderConfig config;
210+
config.multicast_address = "224.0.0.100";
211+
config.port = 12345;
212+
config.ttl = 1; // Local network only
213+
214+
slick::socket::MulticastSender sender("MySender", config);
215+
216+
if (!sender.start())
217+
{
218+
std::cerr << "Failed to start sender" << std::endl;
219+
return -1;
220+
}
221+
222+
// Send data to multicast group
223+
sender.send_data("Hello Multicast World!");
224+
225+
// Check statistics
226+
std::cout << "Packets sent: " << sender.get_packets_sent() << std::endl;
227+
228+
sender.stop();
229+
return 0;
230+
}
231+
```
232+
233+
### Creating a Multicast Receiver
234+
235+
```cpp
236+
#include <slick/socket/multicast_receiver.h>
237+
238+
class MyReceiver : public slick::socket::MulticastReceiverBase<MyReceiver>
239+
{
240+
public:
241+
MyReceiver(const slick::socket::MulticastReceiverConfig& config)
242+
: MulticastReceiverBase("MyReceiver", config) {}
243+
244+
void handle_multicast_data(const std::vector<uint8_t>& data, const std::string& sender_address)
245+
{
246+
std::string message(data.begin(), data.end());
247+
std::cout << "Received from " << sender_address << ": " << message << std::endl;
248+
}
249+
};
250+
251+
int main()
252+
{
253+
slick::socket::MulticastReceiverConfig config;
254+
config.multicast_address = "224.0.0.100";
255+
config.port = 12345;
256+
config.reuse_address = true; // Allow multiple receivers
257+
258+
MyReceiver receiver(config);
259+
260+
if (!receiver.start())
261+
{
262+
std::cerr << "Failed to start receiver" << std::endl;
263+
return -1;
264+
}
265+
266+
// ... receiver runs in background thread
267+
std::this_thread::sleep_for(std::chrono::seconds(30));
268+
269+
receiver.stop();
270+
return 0;
271+
}
272+
```
273+
154274
For more examples, see the [examples/](examples/) directory.
155275

156276
## Testing
@@ -213,11 +333,11 @@ slick_socket/
213333

214334
The library uses a three-file pattern for cross-platform support:
215335

216-
- `component.h` - Base template class with platform-independent interface
336+
- `component.h` - Base class with platform-independent interface
217337
- `component_win32.h` - Windows implementation
218338
- `component_unix.h` - Unix/Linux implementation
219339

220-
This design uses CRTP (Curiously Recurring Template Pattern) for compile-time polymorphism without virtual function overhead.
340+
Most components use CRTP (Curiously Recurring Template Pattern) for compile-time polymorphism without virtual function overhead. `MulticastSender` is implemented as a regular class without CRTP for simpler usage.
221341

222342
## License
223343

examples/CMakeLists.txt

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ target_include_directories(tcp_server_example
99

1010
target_link_libraries(tcp_server_example PRIVATE slick_socket)
1111

12+
set_target_properties(tcp_server_example PROPERTIES
13+
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples
14+
)
15+
1216
add_executable(tcp_client_example
1317
tcp_client_example.cpp
1418
)
@@ -20,6 +24,10 @@ target_include_directories(tcp_client_example
2024

2125
target_link_libraries(tcp_client_example PRIVATE slick_socket)
2226

27+
set_target_properties(tcp_client_example PROPERTIES
28+
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples
29+
)
30+
2331
add_executable(multicast_sender_example
2432
multicast_sender_example.cpp
2533
)
@@ -33,6 +41,10 @@ target_link_libraries(multicast_sender_example
3341
PRIVATE
3442
slick_socket)
3543

44+
set_target_properties(multicast_sender_example PROPERTIES
45+
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples
46+
)
47+
3648
add_executable(multicast_receiver_example
3749
multicast_receiver_example.cpp
3850
)
@@ -46,6 +58,10 @@ target_link_libraries(multicast_receiver_example
4658
PRIVATE
4759
slick_socket)
4860

61+
set_target_properties(multicast_receiver_example PROPERTIES
62+
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples
63+
)
64+
4965
add_executable(multicast_integration_example
5066
multicast_integration_example.cpp
5167
)
@@ -58,3 +74,7 @@ target_include_directories(multicast_integration_example
5874
target_link_libraries(multicast_integration_example
5975
PRIVATE
6076
slick_socket)
77+
78+
set_target_properties(multicast_integration_example PROPERTIES
79+
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/examples
80+
)

examples/multicast_integration_example.cpp

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,6 @@ class MulticastReceiver : public slick::socket::MulticastReceiverBase<MulticastR
2525
std::atomic<int> messages_received_{0};
2626
};
2727

28-
class MulticastSender : public slick::socket::MulticastSenderBase<MulticastSender>
29-
{
30-
public:
31-
MulticastSender(const slick::socket::MulticastSenderConfig& config)
32-
: slick::socket::MulticastSenderBase<MulticastSender>("IntegrationSender", config)
33-
{
34-
}
35-
};
3628

3729
int main()
3830
{
@@ -59,7 +51,7 @@ int main()
5951

6052
// Create receiver and sender
6153
MulticastReceiver receiver(receiver_config);
62-
MulticastSender sender(sender_config);
54+
slick::socket::MulticastSender sender("IntegrationSender", sender_config);
6355

6456
std::cout << "\n1. Starting receiver..." << std::endl;
6557
if (!receiver.start())

examples/multicast_sender_example.cpp

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,6 @@
55
#include <thread>
66
#include <chrono>
77

8-
class MulticastSender : public slick::socket::MulticastSenderBase<MulticastSender>
9-
{
10-
public:
11-
MulticastSender(const slick::socket::MulticastSenderConfig& config)
12-
: slick::socket::MulticastSenderBase<MulticastSender>("MulticastSender", config)
13-
{
14-
}
15-
};
16-
178
int main()
189
{
1910
slick::socket::MulticastSenderConfig config;
@@ -22,7 +13,7 @@ int main()
2213
config.ttl = 1; // Local network only
2314
config.enable_loopback = false;
2415

25-
MulticastSender sender(config);
16+
slick::socket::MulticastSender sender("MulticastSender", config);
2617

2718
std::cout << "Starting multicast sender..." << std::endl;
2819
if (!sender.start())

include/slick/socket/multicast_sender.h

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,19 @@ struct MulticastSenderConfig
2424
int send_buffer_size = 65536; // Socket send buffer size
2525
};
2626

27-
template<typename DerivedT>
28-
class MulticastSenderBase
27+
class MulticastSender
2928
{
3029
public:
31-
explicit MulticastSenderBase(std::string name, const MulticastSenderConfig& config = MulticastSenderConfig());
32-
virtual ~MulticastSenderBase();
30+
explicit MulticastSender(std::string name, const MulticastSenderConfig& config = MulticastSenderConfig());
31+
virtual ~MulticastSender();
3332

3433
// Delete copy operations
35-
MulticastSenderBase(const MulticastSenderBase&) = delete;
36-
MulticastSenderBase& operator=(const MulticastSenderBase&) = delete;
34+
MulticastSender(const MulticastSender&) = delete;
35+
MulticastSender& operator=(const MulticastSender&) = delete;
3736

3837
// Move operations
39-
MulticastSenderBase(MulticastSenderBase&& other) noexcept = default;
40-
MulticastSenderBase& operator=(MulticastSenderBase&& other) noexcept = default;
38+
MulticastSender(MulticastSender&& other) noexcept = default;
39+
MulticastSender& operator=(MulticastSender&& other) noexcept = default;
4140

4241
// Sender control
4342
bool start();
@@ -73,8 +72,6 @@ class MulticastSenderBase
7372
}
7473

7574
protected:
76-
DerivedT& derived() { return static_cast<DerivedT&>(*this); }
77-
const DerivedT& derived() const { return static_cast<const DerivedT&>(*this); }
7875

7976
#if defined(_WIN32) || defined(_WIN64)
8077
using SocketT = SOCKET;

0 commit comments

Comments
 (0)