Encoding-Dependent States

Some states need additional information in order to be constructed and used properly. This can be the case when the encoding has stored some type-erased information, as ztd::text::any_encoding does, or as if you wrote a variant_encoding<utf8le, utf16be, ...>. For example, given a type_erased_encoding like so:

 1class type_erased_encoding {
 2private:
 3        struct erased_state {
 4                virtual ~erased_state () {}
 5        };
 6
 7        struct erased_encoding {
 8                virtual std::unique_ptr<erased_state> create_decode_state() = 0;
 9                virtual std::unique_ptr<erased_state> create_encode_state() = 0;
10
11                virtual ~erased_encoding () {}
12        };
13
14        template <typename Encoding>
15        struct typed_encoding : erased_encoding {
16                Encoding encoding;
17
18                struct decode_state : erased_state {
19                        using state_type = ztd::text::decode_state_t<Encoding>;
20                        state_type state;
21
22                        decode_state(const Encoding& some_encoding)
23                        : state(ztd::text::make_decode_state(some_encoding)) {
24                                // get a decode state from the given encoding
25                        }
26                };
27
28                struct encode_state : erased_state {
29                        using state_type = ztd::text::encode_state_t<Encoding>;
30                        state_type state;
31
32                        decode_state(const Encoding& some_encoding)
33                        : state(ztd::text::make_encode_state(some_encoding)) {
34                                // get a decode state from the given encoding
35                        }
36                };
37
38                typed_encoding(Encoding&& some_encoding)
39                : encoding(std::move(some_encoding)) {
40                        // move encoding in
41                }
42
43                typed_encoding(const Encoding& some_encoding)
44                : encoding(some_encoding) {
45                        // copy encoding in
46                }
47
48                virtual std::unique_ptr<erased_state> create_decode_state() override {
49                        return std::make_unique<decode_state>(encoding);
50                }
51
52                virtual std::unique_ptr<erased_state> create_encode_state() override {
53                        return std::make_unique<encode_state>(encoding);
54                }
55        };
56
57        std::unique_ptr<erased_encoding> stored;
58
59public:
60        template <typename AnyEncoding>
61        type_erased(AnyEncoding&& some_encoding)
62        : stored_ptr(std::make_unique<typed_encoding<std::remove_cvref_t<AnyEncoding>>>(
63                std::forward<AnyEncoding>(some_encoding))
64        ) {
65                // store any encoding in the member unique pointer
66        }
67
68        // ... rest of the implementation
69};

We can see that creating a state with a default constructor no longer works, because the state itself requires more information than can be known by just the constructor itself. It needs access to the wrapped encoding. The solution to this problem is an opt-in when creating your state types by giving your state type a constructor that takes the encoding type:

 1class type_erased_encoding {
 2        // from above, etc. …
 3public:
 4        // public-facing wrappers
 5        struct type_erased_decode_state {
 6        public:
 7                // special constructor!!
 8                type_erased_state (const type_erased_encoding& encoding)
 9                : stored(encoding.stored->create_decode_state()) {
10
11                }
12        private:
13                std::unique_ptr<erased_state> stored;
14        };
15
16        struct type_erased_encode_state {
17        public:
18                // special constructor!!
19                type_erased_state (const type_erased_encoding& encoding)
20                : stored(encoding.stored->create_encode_state()) {
21                        // hold onto type-erased state
22                }
23        private:
24                std::unique_ptr<erased_state> stored;
25        };
26
27        using decode_state = type_erased_state;
28        using encode_state = type_erased_state;
29
30        // ... rest of the Lucky 7 members
31};

These special constructors will create the necessary state using information from the type_erased_encoding to do it properly. This will allow us to have states that properly reflect what was erased when we perform a given higher-level conversion operation or algorithm.

This encoding-aware state-construction behavior is detected by the ztd::text::is_state_independent, ztd::text::is_decode_state_independent, and ztd::text::is_encode_state_independent classifications.

These classifications are used in the ztd::text::make_decode_state and ztd::text::make_encode_state function calls to correctly construct a state object, which is what the API uses to make states for its higher-level function calls. If you are working in a generic context, you should use these functions too when working in this minute details. However, if you’re not working with templates, consider simply using the already-provided ztd::text::any_encoding to do exactly what this example shows, with some extra attention to detail and internal optimizations done on your behalf.