{"id":15047715,"url":"https://github.com/dwmcrobb/libdwm","last_synced_at":"2026-02-12T11:02:04.681Z","repository":{"id":248760050,"uuid":"821620723","full_name":"dwmcrobb/libDwm","owner":"dwmcrobb","description":"C++ library with a focus on serialization but including a syslog wrapper and a simple unit test facility","archived":false,"fork":false,"pushed_at":"2024-10-10T04:41:55.000Z","size":31128,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-22T14:02:18.087Z","etag":null,"topics":["cpp20","logging","serialization","unit-testing"],"latest_commit_sha":null,"homepage":"https://www.mcplex.net","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/dwmcrobb.png","metadata":{"files":{"readme":"README.md","changelog":"ChangeLog","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2024-06-29T01:28:58.000Z","updated_at":"2024-10-10T04:42:00.000Z","dependencies_parsed_at":"2024-08-17T05:31:38.046Z","dependency_job_id":"b23512dd-7589-4d32-8958-71e1507bd0ea","html_url":"https://github.com/dwmcrobb/libDwm","commit_stats":null,"previous_names":["dwmcrobb/libdwm"],"tags_count":43,"template":false,"template_full_name":null,"purl":"pkg:github/dwmcrobb/libDwm","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwmcrobb%2FlibDwm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwmcrobb%2FlibDwm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwmcrobb%2FlibDwm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwmcrobb%2FlibDwm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/dwmcrobb","download_url":"https://codeload.github.com/dwmcrobb/libDwm/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/dwmcrobb%2FlibDwm/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29363629,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-12T08:51:36.827Z","status":"ssl_error","status_checked_at":"2026-02-12T08:51:26.849Z","response_time":55,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp20","logging","serialization","unit-testing"],"created_at":"2024-09-24T21:03:25.897Z","updated_at":"2026-02-12T11:02:04.649Z","avatar_url":"https://github.com/dwmcrobb.png","language":"C++","funding_links":[],"categories":[],"sub_categories":[],"readme":"# libDwm\nThis class library is just a (somewhat random) collection of classes\nI've developed over the years for various applications. Since these\nare used in a variety of places, they're collected here to make reuse\neasy.  It is somewhat of a kitchen sink and contains some old cruft\nI no longer use, but the parts mentioned on this page are heavily\nused in my own code and here to stay.  It is likely that at some\npoint I will refactor other parts and remove a good chunk of classes\nthat I no longer use.\n\n## History\nThe library started with a focus on portable binary I/O.  Namely storing\ndata in network byte order, as well as sending and receiving it over the\nnetwork.  At the time I chose network byte order because the workstations\nand servers I used at the time had big-endian architectures.  If I were\nmaking the same decision today, I'd likely choose little-endian just due\nto the prevalence of little-endian computers in our lives.\n\n## Portable Binary I/O\nThere are classes in the library which provide static functions and\nfunction templates for binary I/O for fundamental types as well as\nstrings.  They also provide I/O for most of the containers in the C++\nstandard library: array, deque, list, map, pair, set, tuple,\nunordered_map, unordered_set, variant and vector.\n\nThe serialization here would likely be easier if we had reflection in\nC++.  But in 2024 we still don't have reflection, hence my technique has\nbeen useful for much longer than I expected.  Having read the proposals\nstill active for C++26 (the last proposal I read was\n[P2996R2](https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2024/p2996r2.html)),\nI suspect I'm going to continue using my technique beyond C++26.  At this\npoint the inertia in my own code is significant, and it doesn't look like\nit will be a small effort to switch to using reflection.\n\nIf we get reflection in C++26, I'll experiment with a new library that\nuses it.\n\nThe classes that implement the I/O:\n\n- [Dwm::StreamIO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1StreamIO.html) - read from and write to iostreams\n- [Dwm::FileIO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1FileIO.html) - read from and write to FILE handles\n- [Dwm::DescriptorIO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1DescriptorIO.html) - read from and write to UNIX descriptors\n- [Dwm::ASIO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1ASIO.html) - read from and write to boost asio sockets\n- [Dwm::BZ2IO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1BZ2IO.html) - read from and write to BZFILE handles (bzip2)\n- [Dwm::GZIO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1GZIO.html) - read from and write to gzFile handles (gzip)\n\nNote that I use length encoding for strings and containers.  Length\nfields are always uint64_t.  Hence there is an 8-byte I/O overhead for\nstrings and most containers, to hold the number of elements.\n\nFor bzip2 and gzip, the library depends on libbz2 and zlib\n(respectively).  It is a shame that the bzip2 library and zlib library\nuse `int` for return values, in the sense that you can't sanely read\nor write anything larger than 2,147,483,647 (2 gigabytes) on platforms\nwhere `int` is 32 bits (which is nearly all 32-bit and 64-bit\nsystems).  However, I didn't want to maintain my own forks of these\nlibraries, so it's just something to be aware of when using\n[Dwm::BZ2IO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1BZ2IO.html)\nand \n[Dwm::GZIO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1GZIO.html).\n\n#### Using the I/O classes with your own types\nTo leverage the library I/O classes for containers of your own types,\nyou simply need to add public members to your class to read and write\nfrom the sources/sinks of interest.  For a long time I used\ninheritance from pure virtual classes, but today I'm migrating away\nfrom it since what's needed can be expressed cleanly with concepts in\nC++20.  I could have done this with function templates and SFINAE long\nago, but resisted since it's difficult for non-experts to read and can\nbe difficult to maintain.  Most C++ programmers understand pure\nvirtual classes and inheritance.  The downside is that it leads to\nmultiple inheritance, which creates headaches for some modern C++\nfeatures like defaulted comparison operators.  Now that we have\nreadable concepts for simple tasks, I have removed the inheritance\nrequirement and can one day remove these virtual classes (which I now\nconsider deprecated but haven't yet marked them as such):\n\n- DescriptorIOCapable, DescriptorReadable and DescriptorWritable\n- FileIOCapable, FileReadable and FileWritable\n- StreamIOCapable, StreamReadable and StreamWritable\n- BZ2IOCapable, BZ2Readable and BZ2Writable\n- GZIOCapable, GZReadable and GZWritable\n- ASIOCapable, ASIOReadable and ASIOWritable\n\nFor [Dwm::StreamIO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1StreamIO.html),\nyou'd add these public members to your class:\n\n    //------------------------------------------------------------------\n    //!  Reads from an istream.  Returns the istream.\n    //------------------------------------------------------------------\n    std::istream \u0026 Read(std::istream \u0026 is);\n    //------------------------------------------------------------------\n    //!  Writes to an ostream.  Returns the ostream.\n    //------------------------------------------------------------------\n    std::ostream \u0026 Write(std::ostream \u0026 os) const;\n\nFor [Dwm::FileIO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1FileIO.html), you'd add these public members to your class:\n\n    //------------------------------------------------------------------\n    //!  Reads from a FILE pointer.  Returns 1 on success, 0 on\n\t//!  failure.\n    //------------------------------------------------------------------\n    size_t Read(FILE * f);\n    //------------------------------------------------------------------\n    //!  Writes to a FILE pointer.  Returns 1 on success, 0 on\n\t//!  failure.\n    //------------------------------------------------------------------\n    size_t Write(FILE * f) const\n\nFor [DescriptorIO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1DescriptorIO.html) you'd add these public members to your class:\n\n    //------------------------------------------------------------------\n    //!  Reads from a file descriptor.  Returns the number of bytes\n\t//!  read on success, -1 on failure.\n    //------------------------------------------------------------------\n    ssize_t Read(int fd);\n    //------------------------------------------------------------------\n    //!  Writes to a file descriptor.  Returns the number of bytes\n\t//!  written on success, -1 on failure.\n    //------------------------------------------------------------------\n    ssize_t Write(int fd) const;\n\nFor [Dwm::BZ2IO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1BZ2IO.html) you'd add these public members to your class:\n\n    //------------------------------------------------------------------\n    //!  Reads from a BZFILE pointer.  Returns the number of bytes\n    //!  read on success, -1 on failure.\n    //------------------------------------------------------------------\n    int BZRead(BZFILE *bzf);\n    //------------------------------------------------------------------\n    //!  Writes to a BZFILE pointer.  Returns the number of bytes\n\t//!  written on success, -1 on failure.\n    //------------------------------------------------------------------\n    int BZWrite(BZFILE *bzf) const;\n\nFor [Dwm::GZIO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1GZIO.html)\nyou'd add these public members to your class:\n\n    //------------------------------------------------------------------\n    //!  Reads from a gzFile.  Returns the number of bytes read\n    //!  on success, -1 on failure.\n    //------------------------------------------------------------------\n    int Read(gzFile gzf);\n    //------------------------------------------------------------------\n    //!  Writes to a gzFile.  Returns the number of bytes written\n    //!  on success, -1 on failure.\n    //------------------------------------------------------------------\n    int Write(gzFile gzf) const;\n\nFor [Dwm::ASIO](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1ASIO.html)\nyou'd add these public members to your class:\n\n    //------------------------------------------------------------------\n    //!  Reads from a boost::asio::ip::tcp::socket.  Returns true on \n\t//!  success.  Returns false and sets @c ec on failure.\n    //------------------------------------------------------------------\n    bool Read(boost::asio::ip::tcp::socket \u0026 s,\n              boost::system::error_code \u0026 ec);\n    //------------------------------------------------------------------\n    //!  Writes to a boost::asio::ip::tcp::socket.  Returns true on\n\t//!  success.  Returns false and sets @c ec on failure.\n    //------------------------------------------------------------------\n    bool Write(boost::asio::ip::tcp::socket \u0026 s,\n               boost::system::error_code \u0026 ec) const;\n    //------------------------------------------------------------------\n    //!  Reads from a boost::asio::local::stream_protocol::socket.\n\t//!  Returns true on success.  Returns false and sets @c ec on \n\t//!  failure.\n    //------------------------------------------------------------------\n    bool Read(boost::asio::local::stream_protocol::socket \u0026 s,\n              boost::system::error_code \u0026 ec);\n    //------------------------------------------------------------------\n    //!  Writes to a boost::asio::local::stream_protocol::socket.\n\t//!  Returns true on success.  Returns false and sets @c ec on \n\t//!  failure.\n    //------------------------------------------------------------------\n    bool Write(boost::asio::local::stream_protocol::socket \u0026 s,\n               boost::system::error_code \u0026 ec) const;\n    //------------------------------------------------------------------------\n    //!  Reads from a boost::asio::generic::stream_protocol::socket.  Returns\n    //!  true on success.  Returns false and sets @c ec on failure.\n    //------------------------------------------------------------------------\n    bool Read(boost::asio::generic::stream_protocol::socket \u0026 s,\n              boost::system::error_code \u0026 ec);\n    //------------------------------------------------------------------------\n    //!  Writes to a boost::asio::generic::stream_protocol::socket.  Returns\n    //!  true on success.  Returns false and sets @c ec on failure.\n    //------------------------------------------------------------------------\n    bool Write(boost::asio::generic::stream_protocol::socket \u0026 s,\n               boost::system::error_code \u0026 ec) const;\n\t\n## SysLogger\nThe [SysLogger](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1SysLogger.html)\nclass wraps `syslog()` functionality in a singleton class.  A \n[Syslog()](https://www.mcplex.net/Software/Documentation/libDwm/DwmSysLogger_8hh.html)\nmacro is provided which will pass `__FILE__` and `__LINE__` along\nautomatically and they will be shown in syslog output if\n[SysLogger::ShowFileLocation()](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1SysLogger.html)\nis set to true.  This is really just a convenience that alleviated me of\nhaving to write `\"... {%s:%d}\", ... __FILE__,__LINE__` every time I wrote a\nsyslog statement.  The\n[SysLogger](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1SysLogger.html)\nclass also encodes the priority in a 3-letter string, which\nis convenient.  For example, `[E]` for something logged at `LOG_ERR`\nlevel.\n\n## UnitAssert: simple unit testing\nWhen I started this library in 1998, we didn't have a lot of great\noptions for C++ unit testing.  Kent Beck's\n[Extreme Programming Explained](https://www.amazon.com/Extreme-Programming-Explained-Embrace-Change/dp/0201616416/)\nwasn't published until October 1999.\n[CppUnit](https://www.freedesktop.org/wiki/Software/cppunit/) didn't\nshow up until 2000, and didn't do what I wanted for my unit testing.\n\nWhat did I want?  I basically just wanted something like the standard\n`assert()` macro, but to not trigger `abort()` and instead keep track of\nthe issue for later reporting.  I also wanted my macro to return `true`\nor `false`, allowing me to skip further processing without throwing an\nexception or aborting.  The whole idea here is something trivially\nsimple to use that lets me just write test code in the same manner as\nnon-test code, just with a liberal use of\n[UnitAssert()](https://www.mcplex.net/Software/Documentation/libDwm/DwmUnitAssert_8hh.html)\ncalls to verify correct operation.  One macro to know and love, one\nsmall set of functions to print results and set test program exit code.\n\nFor examples, all of the unit tests in the `classes/tests` directory use\n[UnitAssert()](https://www.mcplex.net/Software/Documentation/libDwm/DwmUnitAssert_8hh.html)\nand the members of the\n[Assertions](https://www.mcplex.net/Software/Documentation/libDwm/classDwm_1_1Assertions.html)\nclass.\n\n## Platforms\nFreeBSD, linux (Debian-based systems including Ubuntu and Raspberry Pi OS)\nand macOS.\n\nNote that on macOS, I am using MacPorts for dependencies.  Homebrew\nwill probably work, I've just always been a MacPorts user.\n\n## Dependencies\n### Tools\n#### C++ Compiler\nA C++20 compiler is required.  I'm using these at the time of writing:\n- FreeBSD: clang++ 18.1.4\n- Ubuntu 24.04 LTS: g++ 13.2.0\n- macOS: Apple clang version 15.0.0 (clang-1500.3.9.4)\n- Raspberry Pi OS 12(bookworm): g++ 12.2.0\n#### GNU make\n- FreeBSD: `sudo pkg install gmake`\n- Linux: `sudo apt install make`\n#### GNU flex\n- FreeBSD: `sudo pkg install flex`\n- Linux: `sudo apt install flex`\n#### GNU bison\n- FreeBSD: `sudo pkg install bison`\n- Linux: `sudo apt install bison`\n- macOS: `sudo port install bison`\n#### [mkfbsdmnfst](https://github.com/dwmcrobb/mkfbsdmnfst)\nNeeded to build a package on FreeBSD.\n#### [mkdebcontrol](https://github.com/dwmcrobb/mkdebcontrol)\nNeeded to build a package on Debian, Ubuntu and Raspberry Pi OS.\n### Libraries\n#### nlohmann_json\n- FreeBSD: `sudo pkg install nlohmann-json`\n- Linux: `sudo apt install nlohmann-json3-dev`\n- macOS: `sudo port install nlohmann-json`\n#### libxxhash\n- FreeBSD: `sudo pkg install xxhash`\n- Linux: `sudo apt install libxxhash-dev`\n- macOS: `sudo port install xxhashlib`\n#### libncurses (needed for termcap on Linux)\n- Linux: `sudo apt install libncurses-dev`\n#### libpcap\n- Linux: `sudo apt install libpcap-dev`\n- macOS: `sudo port install libpcap`\n#### boost_iostreams\n- FreeBSD: `sudo pkg install boost-libs`\n- Linux: `sudo apt install libboost1.83-all-dev`\n- macOS: `sudo port install boost181`\n#### libtirpc (needed on Linux for XDR routines)\n- Linux: `sudo apt install libtirpc-dev`\n#### libfmt (when std::format isn't available)\n- Linux: `sudo apt install libfmt-dev`\n- FreeBSD: `sudo pkg install libfmt`\n- macOS: `sudo port install libfmt10`\n\n## Build\nThe build requires GNU make (hence on FreeBSD, the make command below\nshould be `gmake`).  It also requires GNU flex and GNU bison.\n\n    ./configure\n    make\n\n## Build a native package\nI normally build a native package to allow me to install the library\nusing the native packaging tools on FreeBSD, Debian-based Linux systems\nand macOS.  This is done with:\n\n    make package\n\nNote that if you want documentation to be included in the build, you\nmust use:\n\n    make BUILD_DOCS=yes package\n\n## Installation\nOnce a package is built, it may be installed using the native installation\ntools on your platform.\n#### FreeBSD\n    pkg install libDwm-0.9.40.pkg\n#### Linux (Debian-based systems)\n    dpkg -i libDwm_0.9.40_amd64.deb\n#### macOS\n    open libDwm-0.9.40.pkg\n\n#### Other options\nYou can stage all of the installation files without creating a package\nby running:\n\n    make tarprep\n\t\nThis will place all of the files for installation in the `staging/usr/local`\ndirectory.  The top level `tarprep` make target is a dependency of the\n`package` target.\n\n## Documentation\nLibrary documentation is available at [www.mcplex.net/Software/Documentation/libDwm](https://www.mcplex.net/Software/Documentation/libDwm/) and may be built in the `doc` directory.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwmcrobb%2Flibdwm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdwmcrobb%2Flibdwm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdwmcrobb%2Flibdwm/lists"}