├── .gitignore ├── Makefile ├── README.md ├── test_nn.cpp ├── license.txt └── nn.hpp /.gitignore: -------------------------------------------------------------------------------- 1 | test 2 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: run_test 2 | 3 | all: run_test 4 | 5 | run_test: test 6 | ./test && echo "test passed" 7 | 8 | test: nn.hpp test_nn.cpp 9 | c++ -std=c++14 test_nn.cpp -o test 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # `nn`: Non-nullable pointers for C++ 2 | 3 | `nn` is a type that helps you enforce at compile time the contract that a given pointer 4 | can't be null. It wraps around raw pointers or smart pointers, and works particularly 5 | well out of the box with `std::unique_ptr` and `std::shared_ptr`. 6 | 7 | Here's an example: 8 | 9 | ```cpp 10 | class Widget : public WidgetBase { 11 | public: 12 | Widget(nn_shared_ptr gadget) : m_gadget(move(gadget)) {} 13 | // ... 14 | private: 15 | nn_shared_ptr m_gadget; 16 | }; 17 | 18 | // nn_make_unique and nn_make_shared always return non-null values 19 | nn_unique_ptr my_widget = nn_make_unique(nn_make_shared()); 20 | 21 | // but what if we have a pointer already and we don't know if it's null? 22 | shared_ptr this_might_be_null = ... 23 | my_widget = nn_make_unique(NN_CHECK_ASSERT(this_might_be_null)); 24 | 25 | // implicit nn_unique_ptr -> nn_shared_ptr works just like unique_ptr -> shared_ptr 26 | nn_shared_ptr shared_widget = move(my_widget); 27 | 28 | // the `nn` implicitly casts away if needed 29 | void save_ownership_somewhere(shared_ptr); 30 | save_ownership_somewhere(shared_widget); 31 | 32 | // implicit upcasts work too 33 | nn_shared_ptr base_ptr = shared_widget; 34 | ``` 35 | 36 | Compile-time checking helps find bugs sooner. At Dropbox we use `nn` pervasively in our 37 | cross-platform C++ codebase. 38 | 39 | ## Compatibility 40 | 41 | nn is C++11 compatible. Tested with GCC 4.8, clang 3.6+ and MSVS 2015. 42 | -------------------------------------------------------------------------------- /test_nn.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2015 Dropbox, Inc. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | #include "nn.hpp" 18 | #include 19 | 20 | // Check usability of class and macros without "using namespace" first 21 | // The remaining code below can be more terse. 22 | static void namespace_test() { 23 | dropbox::oxygen::nn t0 = NN_CHECK_ASSERT(new int(111)); 24 | dropbox::oxygen::nn t1 = NN_CHECK_THROW(new int(222)); 25 | (void)t0; (void)t1; 26 | } 27 | 28 | using namespace dropbox::oxygen; 29 | using std::shared_ptr; 30 | using std::unique_ptr; 31 | using std::unordered_set; 32 | 33 | struct pt_base { virtual ~pt_base() {} }; 34 | struct pt : pt_base { int x; int y; pt(int x, int y) : x(x), y(y) {} }; 35 | struct pt_other : pt_base { int x; int y; pt_other(int x, int y) : x(x), y(y) {} }; 36 | 37 | void take_nn_unique_ptr(nn>) { } 38 | void take_nn_unique_ptr_constref(const nn> &) { } 39 | void take_unique_ptr(unique_ptr) { } 40 | void take_unique_ptr_constref(const unique_ptr &) { } 41 | void take_base_ptr(nn>) { } 42 | void take_nn_raw_ptr(nn) {} 43 | void take_nn_const_raw_ptr(nn) {} 44 | 45 | int main() { 46 | 47 | // Check that we can operate on raw T* properly 48 | nn t = NN_CHECK_ASSERT(new int(7)); 49 | *t = 42; 50 | nn t2 = NN_CHECK_ASSERT(new pt(123, 123)); 51 | t2->x = 1; 52 | delete static_cast(t); 53 | delete static_cast(t2); 54 | delete static_cast(NN_CHECK_ASSERT(new int(7))); 55 | 56 | // Construct and operate on a unique_ptr 57 | nn> p1 = nn_make_unique(pt { 2, 2 }); 58 | p1->x = 42; 59 | *p1 = pt { 10, 10 }; 60 | p1 = nn_make_unique(pt { 1, 1 }); 61 | 62 | // Move a unique_ptr. 63 | take_nn_unique_ptr(nn_make_unique(1)); 64 | take_nn_unique_ptr_constref(nn_make_unique(1)); 65 | take_unique_ptr_constref(nn_make_unique(1)); 66 | take_unique_ptr_constref(nn_make_unique(1)); 67 | nn> i = nn_make_unique(42); 68 | take_nn_unique_ptr_constref(i); 69 | take_unique_ptr_constref(i); 70 | // take_unique_ptr(i); 71 | // take_nn_unique_ptr(i); 72 | take_nn_unique_ptr(std::move(i)); 73 | i = nn_make_unique(42); 74 | take_unique_ptr(std::move(i)); 75 | 76 | // if (p1) return 0; 77 | 78 | // Construct and operate on a shared_ptr 79 | nn> p2 = nn_make_shared(pt { 2, 2 }); 80 | 81 | p2 = nn_make_shared(pt { 3, 3 }); 82 | p2->y = 7; 83 | *p2 = pt { 5, 10 }; 84 | nn> p3 = p2; 85 | shared_ptr normal_shared_ptr = p3; 86 | 87 | // Check that it still works if const 88 | const nn> c1 = nn_make_unique(pt { 2, 2 }); 89 | c1->x = 42; 90 | *c1 = pt { 10, 10 }; 91 | const nn> c2 = p2; 92 | c2->x = 42; 93 | *c2 = pt { 10, 10 }; 94 | shared_ptr m2 = c2; 95 | 96 | // Check assignment 97 | unique_ptr x1; 98 | shared_ptr x2; 99 | int * x3; 100 | { 101 | /* Work around a Clang bug that causes an ambiguous conversion error here. We have 102 | * to static_cast directly - move() is still ambiguous. 103 | * 104 | * http://llvm.org/bugs/show_bug.cgi?id=18359 105 | */ 106 | x1 = static_cast &&>(nn_make_unique(1)); 107 | x2 = static_cast &&>(nn_make_shared(2)); 108 | x3 = NN_CHECK_ASSERT(new int(3)); 109 | } 110 | delete x3; 111 | 112 | // Check conversions to a base class 113 | nn> b1 ( nn_make_unique(pt { 2, 2 }) ); 114 | nn> b2 ( p2 ); 115 | b1 = nn_make_unique(pt { 2, 2 }); 116 | b2 = p2; 117 | take_base_ptr(nn_make_unique(pt { 2, 2 })); 118 | 119 | // Check nn_shared_ptr cast helpers: static cast to derived class 120 | nn> bd1 = nn_make_shared(3, 4); 121 | nn> ds1 = nn_static_pointer_cast(bd1); 122 | assert(ds1->x == 3); 123 | assert(ds1->y == 4); 124 | 125 | // Check nn_shared_ptr cast helpers: dynamic cast to derived class 126 | shared_ptr dd1 = nn_dynamic_pointer_cast(bd1); 127 | assert(dd1); 128 | assert(dd1->x == 3); 129 | assert(dd1->y == 4); 130 | shared_ptr dd_other = nn_dynamic_pointer_cast(bd1); 131 | assert(!dd_other); 132 | 133 | // Check nn_shared_ptr cast helpers: const cast 134 | nn_shared_ptr ncp1 = nn_make_shared(3, 4); 135 | nn_shared_ptr cp1 = nn_make_shared(3, 4); 136 | nn_shared_ptr ncp2 = nn_const_pointer_cast(cp1); 137 | ncp2->x = 11; 138 | assert(cp1->x == 11); 139 | assert(cp1->y == 4); 140 | 141 | // Check construction of smart pointers from raw pointers 142 | int * raw1 = new int(7); 143 | nn raw2 = NN_CHECK_ASSERT(new int(7)); 144 | 145 | unique_ptr u1 (raw1); 146 | nn> u2 (raw2); 147 | 148 | // Test comparison 149 | assert(u1 == u1); 150 | assert(u2 == u2); 151 | assert(!(u1 == u2)); 152 | assert(!(u1 != u1)); 153 | assert(!(u2 != u2)); 154 | assert(u1 != u2); 155 | assert(u1 > u2 || u1 < u2); 156 | assert(u1 >= u2 || u1 <= u2); 157 | 158 | // Test hashing 159 | unordered_set> sset; 160 | sset.emplace(nn_make_shared(1, 2)); 161 | unordered_set> uset; 162 | uset.emplace(nn_make_unique(1, 2)); 163 | unordered_set> rset; 164 | rset.emplace(NN_CHECK_ASSERT(new pt(1, 2))); 165 | 166 | nn> shared = move(u2); 167 | 168 | unique_ptr ud1 (new int(7)); 169 | nn> ud2 ( NN_CHECK_ASSERT(new int(7)) ); 170 | // nn> ud3 = NN_CHECK_ASSERT(new int(7)); 171 | // u2 = raw2; 172 | 173 | int * this_is_null = nullptr; 174 | bool threw = false; 175 | try { 176 | NN_CHECK_THROW(this_is_null); 177 | } catch (const std::runtime_error &) { 178 | threw = true; 179 | } 180 | assert(threw); 181 | 182 | int i1 = 42; 183 | take_nn_raw_ptr(nn_addr(i1)); 184 | take_nn_const_raw_ptr(nn_addr(i1)); 185 | const int i2 = 42; 186 | //take_nn_raw_ptr(nn_addr(i2)); 187 | take_nn_const_raw_ptr(nn_addr(i2)); 188 | 189 | // Ensure namespace_test code is run, and not unused. 190 | namespace_test(); 191 | 192 | // Check aliasing constructor 193 | auto set_ptr = nn_make_shared>(unordered_set{42}); 194 | nn_shared_ptr set_element_ptr(set_ptr, nn_addr(*set_ptr->begin())); 195 | 196 | return 0; 197 | } 198 | -------------------------------------------------------------------------------- /license.txt: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /nn.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* 4 | * Copyright (c) 2015 Dropbox, Inc. 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | namespace dropbox { 26 | namespace oxygen { 27 | 28 | // Marker type and value for use by nn below. 29 | struct i_promise_i_checked_for_null_t {}; 30 | static constexpr i_promise_i_checked_for_null_t i_promise_i_checked_for_null {}; 31 | 32 | // Helper to get the type pointed to by a raw or smart pointer. This can be explicitly 33 | // specialized if need be to provide compatibility with user-defined smart pointers. 34 | namespace nn_detail { 35 | template struct element_type { using type = typename T::element_type; }; 36 | template struct element_type { using type = Pointee; }; 37 | } 38 | 39 | template class nn; 40 | 41 | // Trait to check whether a given type is a non-nullable pointer 42 | template struct is_nn : public std::false_type {}; 43 | template struct is_nn> : public std::true_type {}; 44 | 45 | /* nn 46 | * 47 | * Wrapper around a pointer that is guaranteed to not be null. This works with raw pointers 48 | * as well as any smart pointer: nn, nn>, nn>, 49 | * etc. An nn can be used just like a PtrType. 50 | * 51 | * An nn can be constructed from another nn, if the underlying type would 52 | * allow such construction. For example, nn> can be copied and moved, but 53 | * nn> can only be moved; an nn> can be explicitly 54 | * (but not implicitly) created from an nn; implicit upcasts are allowed; and so on. 55 | * 56 | * Similarly, non-nullable pointers can be compared with regular or other non-nullable 57 | * pointers, using the same rules as the underlying pointer types. 58 | * 59 | * This module also provides helpers for creating an nn from operations that would 60 | * always return a non-null pointer: nn_make_unique, nn_make_shared, nn_shared_from_this, and 61 | * nn_addr (a replacement for operator&). 62 | * 63 | * We abbreviate nn as nn_unique_ptr - it's a little more readable. Likewise, 64 | * nn can be written as nn_shared_ptr. 65 | * 66 | * Finally, we define macros NN_CHECK_ASSERT and NN_CHECK_THROW, to convert a nullable pointer 67 | * to a non-nullable pointer. At Dropbox, these use customized error-handling infrastructure 68 | * and are in a separate file. We've included sample implementations here. 69 | */ 70 | template 71 | class nn { 72 | public: 73 | static_assert(!is_nn::value, "nn> is disallowed"); 74 | 75 | using element_type = typename nn_detail::element_type::type; 76 | 77 | // Pass through calls to operator* and operator-> transparently 78 | element_type & operator*() const { return *ptr; } 79 | element_type * operator->() const { return &*ptr; } 80 | 81 | // Expose the underlying PtrType 82 | operator const PtrType & () const & { return ptr; } 83 | operator PtrType && () && { return std::move(ptr); } 84 | 85 | // Trying to use the assignment operator to assign a nn to a PtrType using the 86 | // above conversion functions hits an ambiguous resolution bug in clang: 87 | // http://llvm.org/bugs/show_bug.cgi?id=18359 88 | // While that exists, we can use these as simple ways of accessing the underlying type 89 | // (instead of workarounds calling the operators explicitly or adding a constructor call). 90 | const PtrType & as_nullable() const & { return ptr; } 91 | PtrType && as_nullable() && { return std::move(ptr); } 92 | 93 | // Can't convert to bool (that would be silly). The explicit delete results in 94 | // "value of type 'nn<...>' is not contextually convertible to 'bool'", rather than 95 | // "no viable conversion", which is a bit more clear. 96 | operator bool() const = delete; 97 | 98 | // Explicitly deleted constructors. These help produce clearer error messages, as trying 99 | // to use them will result in clang printing the whole line, including the comment. 100 | nn(std::nullptr_t) = delete; // nullptr is not allowed here 101 | nn & operator=(std::nullptr_t) = delete; // nullptr is not allowed here 102 | nn(PtrType) = delete; // must use NN_CHECK_ASSERT or NN_CHECK_THROW 103 | nn & operator=(PtrType) = delete; // must use NN_CHECK_ASSERT or NN_CHECK_THROW 104 | 105 | // Semi-private constructor for use by NN_CHECK_ macros. 106 | explicit nn(i_promise_i_checked_for_null_t, const PtrType & arg) : ptr(arg) { assert(ptr); } 107 | explicit nn(i_promise_i_checked_for_null_t, PtrType && arg) : ptr(std::move(arg)) { assert(ptr); } 108 | 109 | // Type-converting move and copy constructor. We have four separate cases here, for 110 | // implicit and explicit move and copy. 111 | template ::value 114 | && !std::is_convertible::value 115 | , int>::type = 0> 116 | explicit nn(const nn & other) : ptr(other.operator const OtherType & ()) {} 117 | 118 | template ::value 121 | && !std::is_convertible::value 122 | && !std::is_pointer::value 123 | , int>::type = 0> 124 | explicit nn(nn && other) : ptr(std::move(other).operator OtherType && ()) {} 125 | 126 | template ::value 129 | , int >::type = 0> 130 | nn(const nn & other) : ptr(other.operator const OtherType & ()) {} 131 | 132 | template ::value 135 | && !std::is_pointer::value 136 | , int>::type = 0> 137 | nn(nn && other) : ptr(std::move(other).operator OtherType && ()) {} 138 | 139 | // A type-converting move and copy assignment operator aren't necessary; writing 140 | // "base_ptr = derived_ptr;" will run the type-converting constructor followed by the 141 | // implicit move assignment operator. 142 | 143 | // Two-argument constructor, designed for use with the shared_ptr aliasing constructor. 144 | // This will not be instantiated if PtrType doesn't have a suitable constructor. 145 | template ::value 148 | , int>::type = 0> 149 | nn(const nn & ownership_ptr, nn target_ptr) 150 | : ptr(ownership_ptr.operator const OtherType & (), target_ptr) {} 151 | 152 | // Comparisons. Other comparisons are implemented in terms of these. 153 | template 154 | friend bool operator==(const nn &, const R &); 155 | template 156 | friend bool operator==(const L &, const nn &); 157 | template 158 | friend bool operator==(const nn &, const nn &); 159 | 160 | template 161 | friend bool operator<(const nn &, const R &); 162 | template 163 | friend bool operator<(const L &, const nn &); 164 | template 165 | friend bool operator<(const nn &, const nn &); 166 | 167 | // ostream operator 168 | template 169 | friend std::ostream & operator<<(std::ostream &, const nn &); 170 | 171 | template 172 | element_type * get() const { return ptr.get(); } 173 | 174 | private: 175 | // Backing pointer 176 | PtrType ptr; 177 | }; 178 | 179 | // Base comparisons - these are friends of nn, so they can access .ptr directly. 180 | template 181 | bool operator==(const nn & l, const R & r) { return l.ptr == r; } 182 | template 183 | bool operator==(const L & l, const nn & r) { return l == r.ptr; } 184 | template 185 | bool operator==(const nn & l, const nn & r) { return l.ptr == r.ptr; } 186 | template 187 | bool operator<(const nn & l, const R & r) { return l.ptr < r; } 188 | template 189 | bool operator<(const L & l, const nn & r) { return l < r.ptr; } 190 | template 191 | bool operator<(const nn & l, const nn & r) { return l.ptr < r.ptr; } 192 | template 193 | std::ostream & operator<<(std::ostream & os, const nn & p) { return os << p.ptr; } 194 | 195 | #define NN_DERIVED_OPERATORS(op, base) \ 196 | template \ 197 | bool operator op(const nn & l, const R & r) { return base; } \ 198 | template \ 199 | bool operator op(const L & l, const nn & r) { return base; } \ 200 | template \ 201 | bool operator op(const nn & l, const nn & r) { return base; } 202 | 203 | NN_DERIVED_OPERATORS(>, r < l) 204 | NN_DERIVED_OPERATORS(<=, !(l > r)) 205 | NN_DERIVED_OPERATORS(>=, !(l < r)) 206 | NN_DERIVED_OPERATORS(!=, !(l == r)) 207 | 208 | #undef NN_DERIVED_OPERATORS 209 | 210 | // Convenience typedefs 211 | template using nn_unique_ptr = nn>; 212 | template using nn_shared_ptr = nn>; 213 | 214 | template 215 | nn_unique_ptr nn_make_unique(Args &&... args) { 216 | return nn_unique_ptr(i_promise_i_checked_for_null, 217 | std::unique_ptr(new T(std::forward(args)...))); 218 | } 219 | 220 | template 221 | nn_shared_ptr nn_make_shared(Args &&... args) { 222 | return nn_shared_ptr(i_promise_i_checked_for_null, 223 | std::make_shared(std::forward(args)...)); 224 | } 225 | 226 | template 227 | class nn_enable_shared_from_this : public std::enable_shared_from_this { 228 | public: 229 | using std::enable_shared_from_this::enable_shared_from_this; 230 | nn_shared_ptr nn_shared_from_this() { 231 | return nn_shared_ptr(i_promise_i_checked_for_null, this->shared_from_this()); 232 | } 233 | nn_shared_ptr nn_shared_from_this() const { 234 | return nn_shared_ptr(i_promise_i_checked_for_null, this->shared_from_this()); 235 | } 236 | }; 237 | 238 | template 239 | nn nn_addr(T & object) { 240 | return nn(i_promise_i_checked_for_null, &object); 241 | } 242 | 243 | template 244 | nn nn_addr(const T & object) { 245 | return nn(i_promise_i_checked_for_null, &object); 246 | } 247 | 248 | /* Non-nullable equivalents of shared_ptr's specialized casting functions. 249 | * These convert through a shared_ptr since nn> lacks the ref-count-sharing cast 250 | * constructor, but thanks to moves there shouldn't be any significant extra cost. */ 251 | template 252 | nn_shared_ptr nn_static_pointer_cast(const nn_shared_ptr & org_ptr) { 253 | auto raw_ptr = static_cast::element_type *>(org_ptr.get()); 254 | std::shared_ptr nullable_ptr(org_ptr.as_nullable(), raw_ptr); 255 | return nn_shared_ptr(i_promise_i_checked_for_null, std::move(nullable_ptr)); 256 | } 257 | 258 | template 259 | std::shared_ptr nn_dynamic_pointer_cast(const nn_shared_ptr & org_ptr) { 260 | auto raw_ptr = dynamic_cast::element_type *>(org_ptr.get()); 261 | if (!raw_ptr) { 262 | return nullptr; 263 | } else { 264 | return std::shared_ptr(org_ptr.as_nullable(), raw_ptr); 265 | } 266 | } 267 | 268 | template 269 | nn_shared_ptr nn_const_pointer_cast(const nn_shared_ptr & org_ptr) { 270 | auto raw_ptr = const_cast::element_type *>(org_ptr.get()); 271 | std::shared_ptr nullable_ptr(org_ptr.as_nullable(), raw_ptr); 272 | return nn_shared_ptr(i_promise_i_checked_for_null, std::move(nullable_ptr)); 273 | } 274 | 275 | } } /* end namespace dropbox::oxygen */ 276 | 277 | namespace std { 278 | template 279 | struct hash<::dropbox::oxygen::nn> { 280 | using argument_type = ::dropbox::oxygen::nn; 281 | using result_type = size_t; 282 | result_type operator()(const argument_type & obj) const { 283 | return std::hash{}(obj.as_nullable()); 284 | } 285 | }; 286 | } 287 | 288 | /* These have to be macros because our internal versions invoke other macros that use 289 | * __FILE__ and __LINE__, which we want to correctly point to the call site. We're looking 290 | * forward to std::source_location :) 291 | * 292 | * The lambdas ensure that we only evaluate _e once. 293 | */ 294 | #include 295 | 296 | // NN_CHECK_ASSERT takes a pointer of type PT (e.g. raw pointer, std::shared_ptr or std::unique_ptr) 297 | // and returns a non-nullable pointer of type nn. 298 | // Triggers an assertion if expression evaluates to null. 299 | #define NN_CHECK_ASSERT(_e) (([&] (typename std::remove_reference::type p) { \ 300 | /* note: assert() alone is not sufficient here, because it might be compiled out. */ \ 301 | assert(p && #_e " must not be null"); \ 302 | if (!p) std::abort(); \ 303 | return dropbox::oxygen::nn::type>( \ 304 | dropbox::oxygen::i_promise_i_checked_for_null, std::move(p)); \ 305 | })(_e)) 306 | 307 | // NN_CHECK_THROW takes a pointer of type PT (e.g. raw pointer, std::shared_ptr or std::unique_ptr) 308 | // and returns a non-nullable pointer of type nn. 309 | // Throws if expression evaluates to null. 310 | #define NN_CHECK_THROW(_e) (([&] (typename std::remove_reference::type p) { \ 311 | if (!p) throw std::runtime_error(#_e " must not be null"); \ 312 | return dropbox::oxygen::nn::type>( \ 313 | dropbox::oxygen::i_promise_i_checked_for_null, std::move(p)); \ 314 | })(_e)) 315 | --------------------------------------------------------------------------------