Skip to content

Commit a0ec0db

Browse files
committed
Add an example for wrapping types, into a new example/ directory.
Move the Hello World example into that directory as well.
1 parent f971ca7 commit a0ec0db

File tree

5 files changed

+162
-8
lines changed

5 files changed

+162
-8
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ set(PUBLIC_HEADERS
6464
add_library(cppcodec OBJECT ${PUBLIC_HEADERS}) # unnecessary for building, but makes headers show up in IDEs
6565
set_target_properties(cppcodec PROPERTIES LINKER_LANGUAGE CXX)
6666
add_subdirectory(tool)
67+
add_subdirectory(example)
6768

6869
if (BUILD_TESTING)
6970
add_subdirectory(test)

example/CMakeLists.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# For cppcodec itself, don't prefer system headers over development ones.
2+
include_directories(BEFORE ${PROJECT_SOURCE_DIR})
3+
4+
add_executable(helloworld helloworld.cpp)
5+
6+
add_executable(type_support_wrapper type_support_wrapper.cpp)
Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,11 @@
2929
#include <iostream>
3030

3131
int main() {
32-
using base32 = cppcodec::base32_crockford;
33-
using base64 = cppcodec::base64_rfc4648;
32+
using base32 = cppcodec::base32_crockford;
33+
using base64 = cppcodec::base64_rfc4648;
3434

35-
std::vector<uint8_t> decoded = base64::decode("YW55IGNhcm5hbCBwbGVhc3VyZQ==");
36-
std::cout << "decoded size (\"any carnal pleasure\"): " << decoded.size() << '\n';
37-
std::cout << base32::encode(decoded) << std::endl; // "C5Q7J833C5S6WRBC41R6RSB1EDTQ4S8"
38-
return 0;
35+
std::vector<uint8_t> decoded = base64::decode("YW55IGNhcm5hbCBwbGVhc3VyZQ==");
36+
std::cout << "decoded size (\"any carnal pleasure\"): " << decoded.size() << '\n';
37+
std::cout << base32::encode(decoded) << std::endl; // "C5Q7J833C5S6WRBC41R6RSB1EDTQ4S8"
38+
return 0;
3939
}

example/type_support_wrapper.cpp

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/**
2+
* This is free and unencumbered software released into the public domain.
3+
* Anyone is free to copy, modify, publish, use, compile, sell, or
4+
* distribute this software, either in source code form or as a compiled
5+
* binary, for any purpose, commercial or non-commercial, and by any
6+
* means.
7+
*
8+
* In jurisdictions that recognize copyright laws, the author or authors
9+
* of this software dedicate any and all copyright interest in the
10+
* software to the public domain. We make this dedication for the benefit
11+
* of the public at large and to the detriment of our heirs and
12+
* successors. We intend this dedication to be an overt act of
13+
* relinquishment in perpetuity of all present and future rights to this
14+
* software under copyright law.
15+
*
16+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17+
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18+
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19+
* IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20+
* OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21+
* ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22+
* OTHER DEALINGS IN THE SOFTWARE.
23+
*
24+
* For more information, please refer to <http://unlicense.org/>
25+
*/
26+
27+
#include <cppcodec/base64_rfc4648.hpp>
28+
#include <iostream>
29+
30+
31+
// This example shows how to wrap a type (here: std::string) in another
32+
// result type in order to modify the standard behavior for that type.
33+
// Use this when you're in a position to wrap the result variable.
34+
//
35+
// This is the more straightforward of two ways for modifying cppcodec's
36+
// behavior for a given type, the other one being able to modify the
37+
// default behavior but also being more complex/intricate.
38+
//
39+
// The overall approach is straightforward: Define a result type with
40+
// push_back(char) and size() methods, implement template specializations
41+
// for init() and finish() for the result type, and call encode()/decode()
42+
// with an object of this type as result parameter.
43+
44+
class string_append_wrapper
45+
{
46+
public:
47+
string_append_wrapper(std::string& backing)
48+
: m_backing(backing)
49+
, m_offset(0)
50+
, m_orig_size(0)
51+
{
52+
}
53+
54+
void init(size_t capacity)
55+
{
56+
m_orig_size = m_backing.size();
57+
m_offset = m_orig_size;
58+
m_backing.resize(m_orig_size + capacity);
59+
}
60+
void finish()
61+
{
62+
m_backing.resize(m_offset);
63+
}
64+
65+
// Methods required for satisfying default result type requirements:
66+
CPPCODEC_ALWAYS_INLINE void push_back(char c) { m_backing[m_offset++] = c; }
67+
CPPCODEC_ALWAYS_INLINE size_t size() const { return m_offset - m_orig_size; }
68+
69+
// Note that the above implementation of push_back() is not the fastest,
70+
// because operator[] for std::string (for C++11 and above) still includes
71+
// a check for whether the size of the string fits into its allocation-less
72+
// character array union.
73+
//
74+
// With C++17 and above, it's legitimate to get the character array as a
75+
// mutable (non-const) char pointer, so this check can be skipped.
76+
// This is implemented via template specialization in cppcodec's
77+
// default behavior for std::string, but omitted here for simplicity.
78+
// If you need that last bit of extra performance, see
79+
// direct_data_access_result_state in cppcodec/data/access.hpp
80+
// for an example of optimal C++17 string access.
81+
82+
private:
83+
std::string& m_backing;
84+
size_t m_offset;
85+
size_t m_orig_size;
86+
};
87+
88+
89+
// init() and finish() must be declared in the cppcodec::data namespace.
90+
namespace cppcodec {
91+
namespace data {
92+
93+
template <> inline void init<string_append_wrapper>(
94+
string_append_wrapper& result, empty_result_state&, size_t capacity)
95+
{
96+
// init() is called to prepare the output buffer. cppcodec will call it
97+
// with the maximum output size, null termination not included.
98+
//
99+
// Any thrown exception will not be caught by cppcodec itself,
100+
// the caller of the encode/decode function is responsible for handling it.
101+
//
102+
// empty_result_state can be ignored in this case because the wrapper type
103+
// can carry all required state internally.
104+
//
105+
// In order to maximize performance, init() should generally try to
106+
// allocate or guarantee the entire output buffer at once, so that
107+
// subsequent calls to push_back() don't result in extra checks (slower)
108+
// or even re-allocations.
109+
110+
result.init(capacity);
111+
}
112+
113+
// Between init() and finish(), cppcodec will call result.push_back(char)
114+
// repeatedly, once for each output character with no rewinding.
115+
// While init() can ask for greater capacity than the final output length,
116+
// cppcodec guarantees that push_back() will never be called too often.
117+
//
118+
// (If you know exactly how long your output is, you could theoretically
119+
// overcommit on capacity while allocating only the exact expected length
120+
// of the output buffer. This is of course dangerous, because you can
121+
// hardly ever know for sure and everyone's often wrong, so don't try it
122+
// unless you have a business-critical reason to reduce/avoid the allocation.)
123+
124+
template <> inline void finish<string_append_wrapper>(
125+
string_append_wrapper& result, empty_result_state&)
126+
{
127+
// finish() is called after encoding/decoding is done.
128+
// Its main purpose is to reduce the size of the result type
129+
// from capacity to the actual (often slightly smaller) output length.
130+
//
131+
// After finish(), cppcodec will assert that result.size() does indeed
132+
// equal the number of times that push_back() has been called.
133+
134+
result.finish();
135+
}
136+
137+
} // namespace data
138+
} // namespace cppcodec
139+
140+
141+
int main() {
142+
using base64 = cppcodec::base64_rfc4648;
143+
144+
std::string result = "Result: ";
145+
string_append_wrapper appender(result);
146+
base64::encode(appender, std::string("any carnal pleasure"));
147+
std::cout << result << std::endl; // "Result: YW55IGNhcm5hbCBwbGVhc3VyZQ=="
148+
return 0;
149+
}

tool/CMakeLists.txt

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,3 @@ add_executable(base64dec base64dec.cpp)
99

1010
add_executable(hexenc hexenc.cpp)
1111
add_executable(hexdec hexdec.cpp)
12-
13-
add_executable(helloworld helloworld.cpp)

0 commit comments

Comments
 (0)