Skip to content

Commit 889cf33

Browse files
committed
api: make a 2-level namespace scheme (#4567)
TL;DR: In main, change the enclosing namespace from `OpenImageIO_v3_1` to a 2-level scheme: `OpenImageIO::v3_1`. This can't be backported to the current release for obvious reasons; it is a forward-looking change only. More ponderous explanation follows: We have for some time had a single namespace that contains the version, defaulting to `OpenImageIO_v3_0` in the current release, for example. A namespace alias, `OIIO`, always aliases the current namespace, so client applications can just say `OIIO::foo` without needing to change code for every minor release (let alone if a build-time option sets up a custom namespace). By bumping the namespace for every minor release, we make the symbol names themselves enforce a rigid ABI compatibility test, so you can't accidentally compile against one minor release and link against an older minor release. This gives us the freedom to break the ABI (link compatibility) for each minor release, without subtle user errors. (Doing it wrong makes a total failure to link, which is hard to miss.) But being able to introduce ABI changes annually by changing the enclosing namespace comes at the cost of a *complete* ABI compatibility break with every minor release. Even classes or functions that haven't change at all will be incompatible by virtue of their changed symbol names. This is an unfortunate limitation, and in an ideal world, we would like downstream users to be able to upgrade to a newer minor release more painlessly, and confident that they could even relink or perhaps compile with a request to use an old ABI. I haven't fully worked out all the details, or even if this is going to be worth the trouble, but I think that a change we can introduce now that will allow more flexibiity in the future is to switch to a 2-level namespace scheme, for example, `OpenImageIO::v3_1` instead of the current `OpenImageIO_v3_1`. The reason this might be helpful in the future is that we can use "inline" namespaces that default to finding things that are in, say, OpenImageIO::v3_2 without needing to specify it explicitly, while giving the ability to explicitly give an alternate inner versioned namespace. I haven't implemented that part here, it's reserved for future expansion (though I have experimented with it, it does work, but I want to discuss separately whether or not to actually do it). Signed-off-by: Larry Gritz <lg@larrygritz.com>
1 parent 703a73e commit 889cf33

3 files changed

Lines changed: 87 additions & 20 deletions

File tree

CMakeLists.txt

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -141,17 +141,29 @@ if (TEX_BATCH_SIZE)
141141
add_compile_definitions (OIIO_TEXTURE_SIMD_BATCH_WIDTH=${TEX_BATCH_SIZE})
142142
endif ()
143143

144-
# Set the default namespace
145-
set (${PROJ_NAME}_NAMESPACE ${PROJECT_NAME} CACHE STRING
146-
"Customized outer namespace base name (version will be added)")
147-
option (${PROJ_NAME}_NAMESPACE_INCLUDE_PATCH
144+
145+
# Namespace settings
146+
#
147+
# The "outer namespace" defaults to the project name, but it can be overridden
148+
# to allow custom builds that put everything inside a unique namespace that
149+
# can't conflict with default builds.
150+
set (${PROJ_NAME}_OUTER_NAMESPACE ${PROJECT_NAME} CACHE STRING
151+
"Customized outer namespace")
152+
set (PROJ_NAMESPACE "${${PROJ_NAME}_OUTER_NAMESPACE}") # synonym
153+
# There is also an inner namespace that is either vMAJ_MIN or vMAJ_MIN_PATCH,
154+
# depending on the setting of ${PROJ_NAME}_INNER_NAMESPACE_INCLUDE_PATCH.
155+
option (${PROJ_NAME}_INNER_NAMESPACE_INCLUDE_PATCH
148156
"Should the inner namespace include the patch number" ${${PROJECT_NAME}_DEV_RELEASE})
149-
set (PROJ_NAMESPACE "${${PROJ_NAME}_NAMESPACE}")
150-
set (PROJ_NAMESPACE_V "${PROJ_NAMESPACE}_v${PROJECT_VERSION_MAJOR}_${PROJECT_VERSION_MINOR}")
151-
if (${PROJ_NAME}_NAMESPACE_INCLUDE_PATCH)
152-
set (PROJ_NAMESPACE_V "${PROJ_NAMESPACE_V}_${PROJECT_VERSION_PATCH}")
157+
if (${PROJ_NAME}_INNER_NAMESPACE_INCLUDE_PATCH)
158+
set (PROJ_VERSION_NAMESPACE "v${PROJECT_VERSION_MAJOR}_${PROJECT_VERSION_MINOR}_${PROJECT_VERSION_PATCH}")
159+
else ()
160+
set (PROJ_VERSION_NAMESPACE "v${PROJECT_VERSION_MAJOR}_${PROJECT_VERSION_MINOR}")
153161
endif ()
154-
message(STATUS "Setting Namespace to: ${PROJ_NAMESPACE_V}")
162+
# PROJ_NAMESPACE_V combines the outer and inner namespaces into one symbol
163+
set (PROJ_NAMESPACE_V "${PROJ_NAMESPACE}_${PROJ_VERSION_NAMESPACE}")
164+
message(STATUS "Outer namespace PROJ_OUTER_NAMESPACE: ${PROJ_NAMESPACE}")
165+
message(STATUS "Inner namespace PROJ_VERSION_NAMESPACE: ${PROJ_VERSION_NAMESPACE}")
166+
message(STATUS "Joint namespace PROJ_NAMESPACE_V: ${PROJ_NAMESPACE_V}")
155167

156168

157169
# Define OIIO_INTERNAL symbol only when building OIIO itself, will not be

src/doc/Doxyfile

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -718,7 +718,7 @@ SHOW_FILES = YES
718718
# Folder Tree View (if specified).
719719
# The default value is: YES.
720720

721-
SHOW_NAMESPACES = YES
721+
SHOW_NAMESPACES = NO
722722

723723
# The FILE_VERSION_FILTER tag can be used to specify a program or script that
724724
# doxygen should invoke to get the current version for each file (typically from
@@ -2185,6 +2185,10 @@ PREDEFINED = DOXYGEN_SHOULD_SKIP_THIS \
21852185
OIIO_HOSTDEVICE= \
21862186
OIIO_NAMESPACE_BEGIN="namespace OIIO {" \
21872187
OIIO_NAMESPACE_END="}" \
2188+
OIIO_NAMESPACE_3_0_BEGIN="namespace OIIO {" \
2189+
OIIO_NAMESPACE_3_0_END="}" \
2190+
OIIO_NS_BEGIN="namespace OIIO {" \
2191+
OIIO_NS_END="}" \
21882192
OIIO_CONSTEXPR17=constexpr \
21892193
OIIO_CONSTEXPR20=constexpr \
21902194
OIIO_IB_DEPRECATE_RAW_PTR:= \

src/include/OpenImageIO/oiioversion.h.in

Lines changed: 61 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -116,16 +116,67 @@
116116
# define OIIO_DISABLE_DEPRECATED 0
117117
#endif
118118

119-
// Establish the name spaces
120-
namespace @PROJ_NAMESPACE_V@ { }
121-
namespace @PROJ_NAME@ = @PROJ_NAMESPACE_V@;
122-
123-
// Macros to use in each file to enter and exit the right name spaces.
124-
#define @PROJ_NAME@_NAMESPACE @PROJ_NAMESPACE_V@
125-
#define @PROJ_NAME@_NAMESPACE_STRING "@PROJ_NAMESPACE_V@"
126-
#define @PROJ_NAME@_NAMESPACE_BEGIN namespace @PROJ_NAMESPACE_V@ {
127-
#define @PROJ_NAME@_NAMESPACE_END }
128-
#define @PROJ_NAME@_NAMESPACE_USING using namespace @PROJ_NAME@;
119+
// Establish the name spaces.
120+
//
121+
// The outer namespace defaults to OpenImageIO, but can be overriden at build
122+
// time. "OIIO" is always defined as an alias to this namespace, so client
123+
// software can always say `OIIO:Foo` without needing to know the custom outer
124+
// namespace.
125+
//
126+
// The primary inner namespace is vMAJ_MIN (or in main, vMAJ_MIN_PAT). The
127+
// outer namespace declares the inner namespace as "inline", so anything in it
128+
// is visible in OIIO by default.
129+
//
130+
// Current API symbols should declare things in the default namespace, which
131+
// has its version incremented with every minor (yearly) release:
132+
//
133+
// // Foo.h
134+
// OIIO_NAMESPACE_BEGIN /* implicitly means current for this version */
135+
// void foo();
136+
// void bar();
137+
// OIIO_NAMESPACE_END
138+
//
139+
// Because the default inner namespace is "inline", client code can just refer
140+
// to `OIIO::Foo`, and `OIIO::v3_1::Foo` will be found (assuming that v3_1 is
141+
// the current inner namespace).
142+
//
143+
// Things can also be put in an explicit inner namespace, such as
144+
//
145+
// OIIO_NS_BEGIN(v3_0)
146+
// // for declarations that should be visible by explicit request only,
147+
// // such as OIIO::v3_0::bar().
148+
// int bar();
149+
// OIIO_NS_END
150+
//
151+
// Currently, everything is defined with OIIO_NAMESPACE_BEGIN/END, and so is
152+
// put in the current-minor-release namespace. But the mechanisms outlined
153+
// above gives us the ability in the future to have multiple ABI (minor
154+
// release) generations of the same facilities existing simultaneously, to
155+
// preserve ABI compatibility even with minor version bumps.
156+
//
157+
158+
namespace @PROJ_NAMESPACE@ {
159+
// Current version's new inner namespace is inline so it's used by default.
160+
inline namespace @PROJ_VERSION_NAMESPACE@ { }
161+
// Legacy namespaces:
162+
namespace v3_0 { }
163+
}
164+
namespace OIIO = @PROJ_NAMESPACE@;
165+
166+
167+
// Macros to declare things in the current version's inline namespace.
168+
#define OIIO_NAMESPACE_BEGIN namespace @PROJ_NAMESPACE@ { inline namespace @PROJ_VERSION_NAMESPACE@ {
169+
#define OIIO_NAMESPACE_END } }
170+
#define OIIO_CURRENT_NAMESPACE @PROJ_NAMESPACE@::@PROJ_VERSION_NAMESPACE@
171+
172+
// Macros for defining legacy namespaces with an explicit version
173+
#define OIIO_NS_BEGIN(ver) namespace @PROJ_NAMESPACE@ { namespace ver {
174+
#define OIIO_NS_END } }
175+
176+
// Specialty macro: Make something ABI compatible with 3.0
177+
#define OIIO_NAMESPACE_3_0 @PROJ_NAMESPACE@_v3_0
178+
#define OIIO_NAMESPACE_3_0_BEGIN namespace OIIO_NAMESPACE_3_0 {
179+
#define OIIO_NAMESPACE_3_0_END }
129180

130181

131182
/// Each imageio DSO/DLL should include this statement:

0 commit comments

Comments
 (0)