Skip to content

Commit d91a92e

Browse files
authored
Reference links/practices added to Safe C++ CG FAQ (#398)
* Reference links/practices added to Safe C++ CG FAQ * Encapsulation examples added to CG FAQ
1 parent 525bec7 commit d91a92e

File tree

1 file changed

+257
-1
lines changed

1 file changed

+257
-1
lines changed

contributor-guide/modules/ROOT/pages/contributors-faq.adoc

+257-1
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,263 @@ No, it is still at the proposal and design refinement stage.
314314

315315
. *What kind of feedback has the proposal garnered so far?*
316316
+
317-
Positive feedback centers on appreciation of the initiative to address longstanding safety concerns in pass:[C++]. More challenging feedback has included concerns about the complexity of integrating new safety features into the existing pass:[C++] framework, balancing enhanced safety with the language's core design features of performance and flexibility, and competition from the https://www.rust-lang.org/[RUST] language.
317+
Positive feedback centers on appreciation of the initiative to address longstanding safety concerns in pass:[C++]. More challenging feedback has included concerns about the complexity of integrating new safety features into the existing pass:[C++] framework, balancing enhanced safety with the language's core design features of performance and flexibility, and competition from the https://www.rust-lang.org/[RUST] and https://developer.apple.com/swift/[Swift] programming languages.
318+
319+
. *Are there references I can read that will help me understand safe concepts and so understand the online discussions?*
320+
+
321+
Yes, in addition to the https://safecpp.org/P3390R0.html[Safe pass:[C++] proposal] from Sean Baxter, the https://herbsutter.com/2024/03/11/safety-in-context/[pass:[C++] safety, in context] blog post, by Herb Sutter, has been written for a broad audience. Also by Herb Sutter, there is a paper entitled https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p3081r0.pdf[Core safety Profiles: Specification, adoptability, and impact].
322+
+
323+
If you refer to the *References* section of any of these papers, you will find a range of books, papers, presentations and the like that delve to various depths into safety issues. For example, the https://open-std.org/JTC1/SC22/WG21/docs/papers/2023/p2816r0.pdf[Safety Profiles:Type-and-resource Safe programming in ISO Standard pass:[C++]], by Bjarne Stroustrup and Gabriel Dos Reis, outlines a talk on the broad spectrum of safety issues in a chattier style than the more formal programming papers - and might be a good place to start!
324+
325+
. *Can you recommend some Boost libraries that demonstrate current best safe-coding practices?*
326+
+
327+
By examining the source code and documentation for any of these libraries, you should be able to educate yourself on a robust approach to safe programming, using current development tools.
328+
+
329+
For _memory safety_, boost:smart_ptr[] provides smart pointer types like `boost::shared_ptr`, `boost::weak_ptr`, and `boost::scoped_ptr` to manage dynamic memory safely and avoid common pitfalls like memory leaks and dangling pointers. boost:pool[] offers memory pooling utilities that efficient managing of memory allocations while minimizing fragmentation. It can help show how to avoid unsafe manual memory management.
330+
+
331+
For _type-safety_, boost:static-assert[] facilitates compile-time checks with `BOOST_STATIC_ASSERT`, ensuring that certain conditions are met during compilation, thus improving type safety. Also, boost:type-traits[] supplies a set of tools for type introspection, enabling safer template programming by providing ways to query and manipulate types.
332+
+
333+
For _resource-safety_ boost:filesystem[] is designed to work with file paths and directories safely, minimizing errors in handling filesystem resources and ensuring proper cleanup. boost:scope_exit[] provides a mechanism for ensuring cleanup of resources (e.g., releasing locks or closing file handles) when a scope is exited, both normally or due to an exception. And boost:interprocess[] facilitates safe and efficient interprocess communication (IPC), managing shared memory and other resources in a resource-safe way.
334+
+
335+
For _thread-safety_ boost:thread[] offers portable thread management and synchronization primitives (such as `boost::mutex`, `boost::lock_guard`) to help developers write thread-safe code. boost:asio[] enables asynchronous I/O operations with an emphasis on thread safety, making it easier to build safe and scalable networked applications. At a lower level, boost:atomic[] provides atomic operations for thread-safe programming, avoiding data races in concurrent applications.
336+
+
337+
For a more general approach to safety, boost:optional[] introduces a way to handle optional values safely, avoiding issues like null pointer dereferencing.
338+
boost:variant2[] provides a type-safe `union` type, ensuring that only one active type is stored at any time, preventing type misuse errors. boost:coroutine2[] implements stackful coroutines with resource management in mind, preventing unsafe usage patterns.
339+
340+
. *Using current development tools what are the design principles of safe programming?*
341+
+
342+
Current best practices start with the use of static and compile-time checks to enforce constraints early. For resource-safety the idiom is _Resource Acquisition Is Initialization_ (RAII). This idiom ties the lifetime of a resource to a programming object, so that when the object is created the resource is initialized, and when the object is destroyed the resource is released. However, the central theme of current safety is _Encapsulation_ - the encapsulation of known unsafe operations in well-tested, robust, reusable abstractions, for example:
343+
344+
*** Instead of exposing raw pointers, use smart pointers or custom encapsulation to ensure safe memory management:
345+
+
346+
[source,cpp]
347+
```
348+
\\ Unsafe code
349+
350+
int* allocateArray(size_t size) {
351+
return new int[size];
352+
}
353+
354+
void useArray() {
355+
int* arr = allocateArray(10);
356+
// Forgetting to delete could cause memory leaks.
357+
arr[0] = 42;
358+
// No bounds checking.
359+
delete[] arr;
360+
}
361+
362+
\\ Safe encapsulation
363+
364+
#include <vector>
365+
#include <memory>
366+
367+
class SafeArray {
368+
private:
369+
std::unique_ptr<int[]> data;
370+
size_t size;
371+
372+
public:
373+
SafeArray(size_t size) : data(std::make_unique<int[]>(size)), size(size) {}
374+
375+
int& operator[](size_t index) {
376+
if (index >= size) {
377+
throw std::out_of_range("Index out of range");
378+
}
379+
return data[index];
380+
}
381+
382+
size_t getSize() const { return size; }
383+
};
384+
385+
void useSafeArray() {
386+
SafeArray arr(10);
387+
arr[0] = 42; // Safe access
388+
try {
389+
arr[10] = 13; // Throws an exception
390+
} catch (const std::out_of_range& e) {
391+
std::cerr << e.what() << std::endl;
392+
}
393+
}
394+
395+
```
396+
+
397+
*** Handle file operations safely by ensuring that the file is properly closed after use.
398+
+
399+
[source,cpp]
400+
```
401+
\\ Unsafe code
402+
403+
void writeFile(const std::string& filename) {
404+
FILE* file = fopen(filename.c_str(), "w");
405+
if (file) {
406+
fputs("Hello, World!", file);
407+
// Forgetting fclose could cause resource leaks.
408+
}
409+
}
410+
411+
\\ Safe encapsulation
412+
413+
#include <fstream>
414+
#include <string>
415+
416+
class FileHandler {
417+
private:
418+
std::ofstream file;
419+
420+
public:
421+
explicit FileHandler(const std::string& filename) {
422+
file.open(filename, std::ios::out);
423+
if (!file) {
424+
throw std::ios_base::failure("Failed to open file");
425+
}
426+
}
427+
428+
~FileHandler() {
429+
if (file.is_open()) {
430+
file.close();
431+
}
432+
}
433+
434+
void write(const std::string& content) {
435+
if (!file) {
436+
throw std::ios_base::failure("File not open");
437+
}
438+
file << content;
439+
}
440+
};
441+
442+
void safeWriteFile(const std::string& filename) {
443+
try {
444+
FileHandler fh(filename);
445+
fh.write("Hello, World!");
446+
} catch (const std::exception& e) {
447+
std::cerr << "Error: " << e.what() << std::endl;
448+
}
449+
}
450+
451+
```
452+
+
453+
*** Prevent race conditions by wrapping shared resources in a thread-safe interface.
454+
+
455+
[source,cpp]
456+
```
457+
\\ Unsafe code
458+
459+
#include <iostream>
460+
#include <thread>
461+
#include <vector>
462+
463+
int counter = 0;
464+
465+
void incrementCounter() {
466+
for (int i = 0; i < 1000; ++i) {
467+
++counter; // Race condition
468+
}
469+
}
470+
471+
void unsafeThreads() {
472+
std::thread t1(incrementCounter);
473+
std::thread t2(incrementCounter);
474+
t1.join();
475+
t2.join();
476+
std::cout << "Counter: " << counter << std::endl; // Undefined behavior
477+
}
478+
479+
\\ Safe encapsulation
480+
481+
#include <iostream>
482+
#include <thread>
483+
#include <vector>
484+
#include <mutex>
485+
486+
class ThreadSafeCounter {
487+
private:
488+
int counter = 0;
489+
std::mutex mtx;
490+
491+
public:
492+
void increment() {
493+
std::lock_guard<std::mutex> lock(mtx);
494+
++counter;
495+
}
496+
497+
int get() const {
498+
return counter;
499+
}
500+
};
501+
502+
void safeThreads() {
503+
ThreadSafeCounter counter;
504+
505+
auto worker = [&counter]() {
506+
for (int i = 0; i < 1000; ++i) {
507+
counter.increment();
508+
}
509+
};
510+
511+
std::thread t1(worker);
512+
std::thread t2(worker);
513+
t1.join();
514+
t2.join();
515+
516+
std::cout << "Counter: " << counter.get() << std::endl; // Guaranteed correct result
517+
}
518+
519+
520+
```
521+
+
522+
*** Instead of using raw sockets, encapsulate them in a class that ensures proper resource cleanup.
523+
+
524+
[source,cpp]
525+
```
526+
\\ Unsafe code
527+
528+
#include <sys/socket.h>
529+
#include <unistd.h>
530+
531+
int createSocket() {
532+
int sock = socket(AF_INET, SOCK_STREAM, 0);
533+
if (sock == -1) {
534+
perror("Socket creation failed");
535+
return -1;
536+
}
537+
// Forgetting close(sock) could cause resource leaks.
538+
return sock;
539+
}
540+
541+
\\ Safe encapsulation
542+
543+
#include <sys/socket.h>
544+
#include <unistd.h>
545+
#include <stdexcept>
546+
547+
class SafeSocket {
548+
private:
549+
int sock;
550+
551+
public:
552+
SafeSocket() {
553+
sock = socket(AF_INET, SOCK_STREAM, 0);
554+
if (sock == -1) {
555+
throw std::runtime_error("Socket creation failed");
556+
}
557+
}
558+
559+
~SafeSocket() {
560+
if (sock != -1) {
561+
close(sock);
562+
}
563+
}
564+
565+
int getSocket() const {
566+
return sock;
567+
}
568+
};
569+
570+
571+
```
572+
+
573+
By wrapping low-level operations in safe abstractions, you make the code easier to use and much harder to misuse!
318574

319575
[[security]]
320576
== Security

0 commit comments

Comments
 (0)