This title seems to receive a lot of criticism. However, I have found this notion to be difficult to search for. It is also relatively new in C++ land, where schools are still teaching C++11
.
Motivation and Context to Avoid the XY-Problem
Right now, I am instrumenting out an embedded device project with concepts
as part of the reverse engineering (and learning) process. The problem with device libraries and device APIs is that there are all sorts of programming no-no's that boil out of the literal device (or product line of devices) capabilities:
- Hard coded settings
- extensions
- macros
- specific bundles or subsets of device functionality
- which leverage macros and helper classes to build structs (structs building structs)
- no simplifying assumptions: massive error handling baggage for cross-platforming and device testing
- etc.
The code winds up -- no matter how hard the devs tried, I know there is no better result possible -- being very verbose and complicated. In other words, it is functionally simple, but hard to grok.
Typically, I might form structs
to wrap bundles and hide away hard-coded content, but now I am using concepts
because they allow me to construct my interfacing namespace that can be changed or updated by me (a lone developer) much more efficiently in terms of knowledge. The less I have to know about the device to use the device, the better.
With concepts, I can also protect code I write which depends on my conceptualized content from change propagation by the device manufacturer and api providers.
In leveraging these concepts, I am trying to make early choices about how I structure my code.
In order to make a decision, I need to know whether it is possible for concepts
to be written atomically, then composed into conceptual structures
. For example:
// real code
// Concepts
template < typename ContextInfo>
concept ExtensibleDeviceContext = requires(ContextInfo & contextInfo, const char * name,
bool optional, void * devFeatures)
{
{ contextInfo.addDeviceExtension(name, optional, devFeatures)};
};
template< typename ContextInfo>
concept ExtensibleInstanceContext = requires(ContextInfo & contextInfo, const char * name,
bool optional)
{
{ contextInfo.addInstanceLayer(name, optional) };
{ contextInfo.addInstanceExtension(name, optional) };
};
// Some dummy functions
template<ExtensibleDeviceContext Cx>
void foo(Cx &context, const char *name, bool optional, void *devFeatures)
{
context.addDeviceExtension(name, optional, devFeatures, version);
context.addDeviceExtension(FOO_MACRO_1);
}
template<ExtensibleInstanceContext Cx>
void bar(Cx &context, const char *name, bool optional)
{
context.addInstanceLayer(name, optional);
context.addInstanceExtension(BAR_MACRO);
}
And what I am trying to do is, essentially, compose the concepts into a bundle of concepts within the template context "<...>
":
// psuedo-code: what I am trying to achieve
template<typename PortInfo: {ExtensibleInstanceContext, ExtensibleDeviceContext}>
void foobar(
PortInfo port,
const char *portName,
bool optional,
const char *devName,
bool devOptional,
void *devFeatures
)
{
foo(port, devName, devOptional, devFeatures);
bar(port, portName, optional);
};
Is this sort of concept
composition possible? If so, is it possible to do it in the template grammar, outlined above? If not, is there some "better" or "intended" way this should be done instead?