I currently have the following code:
template< class Obj, class ObjResult >
CLStatus convertObjToResult2( const Obj & xFrom, ObjResult & xTo )
{
CLStatus eStatus = CLSTATUS_SUCCESS;
switch ( xTo.eType )
{
case CEPTFull:
xTo.xData.xFull = xFrom;
break;
case CEPTBrief:
eStatus = Convert( xFrom, xTo.xData.xBrief );
break;
default:
eStatus = CLSTATUS_INVALIDPROJECTIONTYPE;
}
return eStatus;
}
template< class Obj, class ObjResult >
CLStatus convertObjToResult1( const Obj & xFrom, ObjResult & xTo )
{
CLStatus eStatus = CLSTATUS_SUCCESS;
switch ( xTo.eType )
{
case CEPTFull:
xTo.xData.xFull = xFrom;
break;
default:
eStatus = CLSTATUS_INVALIDPROJECTIONTYPE;
}
return eStatus;
}
All ObjResults have an xFull, but only some have an xBrief, where xData is a union. This resulted in me writing the two different templates above, but it would be great if I could somehow have just one template.
I can't simply use convertObjToResult2, since it will fail to compile with object types that do not have an xBrief. I looked at this answer to see if it would help, but I don't understand at all what it's doing.
Since C++ does not and won't have a static if feature, you need a work around.
If you can't overload Convert as you said in comments, I thought of a layer above it which can be specialized depending on if ObjResult has the member or not.
template <class Obj, class ObjResult, class = void>
struct ConvertIfHasBrief {
static auto Convert(Obj const &, ObjResult &) -> CLStatus {
return {};// dymmy value, not used
}
};
template <class Obj, class ObjResult>
struct ConvertIfHasBrief <Obj, ObjResult,
std::void_t<decltype(std::declval<ObjResult>().xData.xBrief)>> {
static auto Convert(Obj const &xFrom, ObjResult &xTo) {
return ::Convert(xFrom, xTo.xData.xBrief);
}
};
template< class Obj, class ObjResult>
CLStatus convertObjToResult( const Obj & xFrom, ObjResult & xTo )
{
CLStatus eStatus = CLSTATUS_SUCCESS;
switch ( xTo.eType )
{
case CEPTFull:
xTo.xData.xFull = xFrom;
break;
case CEPTBrief:
eStatus = ConvertIfHasBrief<Obj, ObjResult>::Convert(xFrom, xTo);
break;
default:
eStatus = CLSTATUS_INVALIDPROJECTIONTYPE;
}
return eStatus;
}
std::void_t is not yet part of the standard, but the implementation is simple and can be found on the linked page. Just be sure not to declare it on the std namespace.
proof it works
Related
Is it possible to extend all(?) existing C++ JSON libraries with XPath/XPointer or subset with just one C++ implementation? At least those with iterators for object and array values?
I have reviewed three C++ JSON libraries (reviewing nlohmann, Boost.JSON and RapidJSON) to see the internals and check their search functionality. Some have implemented Json pointer. Json pointer is basic, almost like working with json as a name-value list.
XML has XPath and XPointer searches and rules are standardized. With XPath and XPointer you can do more.
One reason to reviewing these libraries was to see if it is possible to extend any of them with better search functionality. Or might it be possible to extend all(?) C++ JSON libraries at once?
A longer text describing this can be found here, trying to be brief.
I tried to do one traverse method that selects json values with one specific property name and that method should work an all tested JSON libraries. If I got that to work it may be possible to add more search logic and get it to work on almost all C++ JSON.
I got this C++ templated function to work an all tested json libraries. It can walk the JSON tree and select json values on all tested libraries.
What is needed to is to implement specializations of is_object, is_array, compare_name, get_value, begin and end. That are just one liners so it's easy.
template<typename json_value>
bool is_object( const json_value* p )
{ static_assert(sizeof(json_value) == 0, "Only specializations of is_object is allowed"); }
template<typename json_value>
bool is_array( const json_value* p )
{ static_assert(sizeof(json_value) == 0, "Only specializations of is_array is allowed"); }
template<typename iterator>
bool compare_name( iterator it, std::string_view stringName )
{ static_assert(sizeof(it) == 0, "Only specializations of compare_name is allowed"); }
template<typename iterator, typename json_value>
const json_value* get_value( iterator it )
{ static_assert(sizeof(it) == 0, "Only specializations of get_value is allowed"); }
template<typename iterator, typename json_value>
iterator begin( const json_value& v ) { return std::begin( v ); }
template<typename iterator, typename json_value>
iterator end( const json_value& v ) { return std::end( v ); }
// ------------------------------------------------
// Selects all json values that match property name
template<typename json_value, typename object_iterator,typename array_iterator = object_iterator>
uint32_t select( const json_value& jsonValue, std::string_view stringQuery, std::vector<const json_value*>* pvectorValue = nullptr )
{ assert( is_object( &jsonValue ) || is_array( &jsonValue ) );
uint32_t uCount = 0;
if( is_object( &jsonValue ) == true ) // found object ?
{
for( auto it = begin<object_iterator,json_value>( jsonValue ); it != end<object_iterator,json_value>( jsonValue ); it++ )
{
if( is_object( get_value<object_iterator,json_value>( it ) ) == true )
{ // found object, scan it
auto value = get_value<object_iterator,json_value>( it );
uCount += select<json_value,object_iterator>( *value, stringQuery, pvectorValue );
}
else if( is_array( get_value<object_iterator,json_value>( it ) ) == true )
{ // found array, scan it
auto parray = get_value<object_iterator,json_value>( it );
uCount += select<json_value,object_iterator,array_iterator>( *parray, stringQuery, pvectorValue );
}
else if( compare_name<object_iterator>( it, stringQuery ) == true )
{ // property name matches, store value if pointer to vector
if( pvectorValue != nullptr ) pvectorValue->push_back( get_value<object_iterator,json_value>( it ) );
uCount++;
}
}
}
else if( is_array( &jsonValue ) == true ) // found array
{
for( auto it = begin<array_iterator,json_value>( jsonValue ); it != end<array_iterator,json_value>( jsonValue ); it++ )
{
if( is_object( get_value<array_iterator,json_value>( it ) ) == true )
{ // found object, scan it
auto value = get_value<array_iterator,json_value>( it );
uCount += select<json_value,object_iterator>( *value, stringQuery, pvectorValue );
}
else if( is_array( get_value<array_iterator,json_value>( it ) ) == true )
{ // found array, scan it
auto parray = get_value<array_iterator,json_value>( it );
uCount += select<json_value,object_iterator,array_iterator>( *parray, stringQuery, pvectorValue );
}
}
}
return uCount;
}
if this works and if I haven't forgot something, shouldn't it be possible to extend all libraries with just one implementation? The additional logic for XPath and XPointer is not dependent on the implementation of these C++ JSON libraries.
Am I missing something
I found this function: https://dlang.org/phobos/std_bitmanip.html#.read
T read(T, Endian endianness = Endian.bigEndian, R)(ref R range)
if (canSwapEndianness!T && isInputRange!R && is(ElementType!R : const(ubyte)));
But I can't understand how to make reading from data into structure.
Is it possible to use something like this?
struct MyType {
uint value;
this(ubyte act) { // wtf.peek!ubyte();
switch (act) {
case 0:
value = wtf.read!uint();
break;
case 1:
wtf.read!ubyte(); // seek
value = wtf.read!uint() | 0x7;
break;
...
}
}
}
...
buffer.read!MyType();
I don't fully understand the structure of your code - where does wtf come from?
It seems to me you've misunderstood what to pass to MyType's constructor - you probably should pass it the range, and read off of that with std.bitmanip.read. I might have misunderstood your code, though:
import std.range;
import std.bitmanip;
struct MyType {
uint value;
this(R)(auto ref R rng) if (isInputRange!R && is(ElementType!R : const(ubyte)))
{
auto act = rng.peek!ubyte;
switch (act) {
case 0:
value = rng.read!uint;
break;
case 1:
rng.read!ubyte;
value = rng.read!uint;
break;
// ...
default: assert(0);
}
}
}
unittest {
ubyte[] buffer = [0x01,0x12,0x34,0x56,0x78];
auto a = MyType(buffer);
assert(buffer.length == 0);
assert(a.value == 0x12345678);
}
As you can see, I simply call MyType's constructor with the array as parameter, but you could also wrap it in a read function if you really want to:
alias read = std.bitmanip.read;
T read(T : MyType, R)(auto ref R range)
if (isInputRange!R && is(ElementType!R : const(ubyte)))
{
return MyType(range);
}
unittest {
ubyte[] buffer = [0x01,0x12,0x34,0x56,0x78];
auto a = buffer.read!MyType;
assert(buffer.length == 0);
assert(a.value == 0x12345678);
}
When reading codes, we will find some functions like this.
g_spawn_async(NULL, new_argv, NULL, G_SPAWN_SEARCH_PATH, NULL, NULL, NULL, NULL);
I think nobody can figure out what is the meaning of every parameter. In order to understand the code, we have to find the declaration of the function.
gboolean g_spawn_async (const gchar *working_directory,
gchar **argv,
gchar **envp,
GSpawnFlags flags,
GSpawnChildSetupFunc child_setup,
gpointer user_data,
GPid *child_pid,
GError **error);
How can we call a function like the following format in C++?
g_spawn_async(working_directory=NULL,
argv=new_argv,
envp=NULL,
flags=G_SPAWN_SEARCH_PATH,
child_setup=NULL,
user_data=NULL,
child_pid=NULL,
error=NULL);
I think this one will be more readable and I can understand the code without looking for the declaration of the function.
I know Python can do this. How can C++ do this?
C++ doesn't support this natively, so you can't do it with just any old existing function. If you're creating your own API though, you can use what's called the Named Parameter Idiom to emulate it. The example from the link:
File f = OpenFile("foo.txt")
.readonly()
.createIfNotExist()
.appendWhenWriting()
.blockSize(1024)
.unbuffered()
.exclusiveAccess();
This is not possible in C or C++.
I understand your pains with this. I personally think that it is a sign of bad design to have a function take over 9000 arguments, especially if most of them are NULL or placeholder values. Many POSIX-standardized functions for example take some kind of struct that accumulates all necessary values into one, easy to understand argument.
No, this can't be done. But you can assign the NULL values to the variables and then pass them as parameters if it helps with your readability!
g_spawn_async(working_directory, argv, envp,flags,child_setup , user_data, child_pid, error);
The BOOST parameter library can help you. It works well and is portable....
See http://www.boost.org/doc/libs/1_54_0/libs/parameter/doc/html/index.html
It's certainly possible. It's not even particularly difficult,
but it does involve a lot of code. Something like the following
could be used:
enum MyFuncParamId
{
myA,
myB,
myC,
myD,
unknown
};
class MyFuncParam
{
union OneParam
{
double aOrB;
C c;
int d;
OneParam() {}
~OneParam() {}
};
OneParam myParam;
MyFuncParamId myId;
public:
MyFuncParam( MyFuncParamId id, double value )
: myId( id )
{
switch ( myId ) {
case myA:
case myB:
myParam.aOrB = value;
break;
case myC:
assert( 0 );
abort();
case myD:
myParam.d = value;
break;
}
}
MyFuncParam( MyFuncParamId id, C const& value )
: myId( id )
{
switch ( myId ) {
case myA:
case myB:
case myD:
assert( 0 );
abort();
case myC:
new (&myParam.c) C( value );
break;
}
}
MyFuncParam( MyFuncParamId id, int value )
: myId( id )
{
switch ( myId ) {
case myA:
case myB:
myParam.aOrB = value;
break;
case myC:
assert( 0 );
abort();
case myD:
myParam.d = value;
break;
}
}
MyFuncParam( MyFuncParam const& other )
: myId( other.myId )
{
switch ( myId ) {
case myA:
case myB:
myParam.aOrB = other.myParam.aOrB;
break;
case myC:
new (&myParam.c) C( other.myParam.c );
break;
case myD:
myParam.d = other.myParam.d;
break;
}
}
~MyFuncParam()
{
switch( myId ) {
case myC:
myParam.c.~C();
break;
}
}
MyFuncParam& operator=( MyFuncParam const& ) = delete;
friend class MyFuncParamGroup;
};
class MyFuncRouter
{
MyFuncParamId myId;
public:
MyFuncRouter( MyFuncParamId id ) : myId( id ) {}
MyFuncParam operator=( double value )
{
return MyFuncParam( myId, value );
}
MyFuncParam operator=( C const& value )
{
return MyFuncParam( myId, value );
}
MyFuncParam operator=( int value )
{
return MyFuncParam( myId, value );
}
};
static MyFuncRouter a( myA );
static MyFuncRouter b( myB );
static MyFuncRouter c( myC );
static MyFuncRouter d( myD );
struct MyFuncParamGroup
{
bool aSet;
bool bSet;
bool cSet;
bool dSet;
double a;
double b;
C c;
int d;
MyFuncParamGroup()
: aSet( false )
, bSet( false )
, cSet( false )
, dSet( false )
{
}
void set( MyFuncParam const& param )
{
switch ( param.myId ) {
case myA:
assert( !aSet );
aSet = true;
a = param.myParam.aOrB;
break;
case myB:
assert( !bSet );
bSet = true;
b = param.myParam.aOrB;
break;
case myC:
assert( !cSet );
cSet = true;
c = param.myParam.c;
break;
case myD:
assert( !dSet );
dSet = true;
d = param.myParam.d;
break;
}
}
};
void
myFunc(
MyFuncParam const& p1,
MyFuncParam const& p2,
MyFuncParam const& p3,
MyFuncParam const& p4)
{
MyFuncParamGroup params;
params.set( p1 );
params.set( p2 );
params.set( p3 );
params.set( p4 );
std::cout << "a = " << params.a
<< ", b = " << params.b
<< ", c = " << params.c
<< ", d = " << params.d
<< std::endl;
}
Notes:
I've used C++11 here. The same thing can be done in earlier
versions of C++, by replacing the type C in the union with
unsigned char c[sizeof( C )];, adding something to the union
to ensure correct alignment (if necessary), and a lot of type
casting.
This would be a lot simpler with boost::variant (instead of
the union) and boost::optional (in MyFuncParamGroup).
I didn't have Boost available, so I did most of what they do
explicitly. (Which of course, makes the code a lot longer.)
I've intentionally used two parameters with the same type,
and a user defined type (C) with non-trivial constructors,
to show how these are handled.
You'd probably want a bit more encapsulation, and a bit more
error checking.
But the real question is: do you really want to go this route?
The amount of code increases linearly with the number of
parameters. And with any decent editor, you can temporarily put
the parameter list from the function declaration to the right of
your screen, and fill out the parameters to the left, directly
along side of the parameter declaration. (In gvim, I'll usually
use block editing mode for this, but :vsplit can also be
used.)
Named parameters are very useful and I was even considering that in a language they should be the only way to call a function except for a single obvious parameter if that is present.
sin(x) // the obvious main parameter
sin(x, unit=DEGREE) // any other must be named
C++ unfortunately doesn't have them.
More importantly C++ lacks the metaprogramming ability needed to be able to implement them.
While there are tricks that can try to somewhat mimic named parameters and even if the resulting code can look almost reasonable, what is totally indecent is the code you need to write to get that simulation and that there is no way in C++ to generate that code.
What I mean is that while it's possible to write a function accepting named parameters emulated reasonably, the code for the function itself is hideous and cannot be automatically generated in C++. This means that no one will write functions that way so the feature is still not present.
My guess is that named parameters are missing because of a mix of ignorance (they existed way before C++ was invented, probably even before C) and of pride (even in C++11 the language metaprogramming abilities are pathetic and things trivial like say enumerating at compile time the members of a structure or the parameters of a function is impossible).
I'm implementing a multi-dimensional tensor for a linear algebra library in D and this is basically what I was aiming to for the base class:
class Tensor( T_scalar, T_dimensions ..., int T_storageOrder = StorageOrder.columnMajor )
{
}
In the idea, the user can define the characteristics of the tensor through template parameters and as a result I can deduce as many things as possible during compile-time, a bit like Eigen does.
Unfortunately the compiler is not so happy with that definition and triggers an error such as:
Tensor(T_scalar,T_args...,int T_storageOrder = StorageOrder.columnMajor) template tuple parameter must be last one
I'm not so sure why this restriction is in place but I ended up doing what I consider being a hack... basically, having defined the StorageOrder as an enum allows me to check if the last argument of the template tuple parameter matches one of the values from the enum, and if so I can use it to set the value of the StorageOrder for that tensor, otherwise I set it up with a default value.
enum StorageOrder : int
{
columnMajor = -1,
rowMajor = -2
}
class Tensor( T_scalar, T_args ... )
{
private:
alias TensorTraits!( T_scalar, T_args ) traits;
alias traits.dimensions T_dimensions;
alias traits.storageOrder T_storageOrder;
}
struct TensorTraits( T_scalar, T_args ... )
if ( areTemplateParametersValid!( T_scalar, T_args )() )
{
static immutable auto dimensions = mixin( extractDataFromTemplateTupleParameter.dimensions );
static immutable int storageOrder = extractDataFromTemplateTupleParameter.storageOrder;
private:
static auto extractDataFromTemplateTupleParameter()
{
Tuple!( string, "dimensions", int, "storageOrder" ) templateTupleParameterData;
static if ( T_args[$ - 1] == StorageOrder.columnMajor || T_args[$ - 1] == StorageOrder.rowMajor )
{
alias TypeTuple!( T_args[0 .. $ - 1] ) dimensionsTuple;
templateTupleParameterData.storageOrder = T_args[$ - 1];
}
else
{
alias TypeTuple!( T_args ) dimensionsTuple;
templateTupleParameterData.storageOrder = StorageOrder.columnMajor;
}
static assert( dimensionsTuple.length > 0,
"No dimensions have been defined." );
foreach ( dimension; dimensionsTuple )
{
static assert( isIntegral!( typeof( dimension ) ),
"Dimensions sizes needs to be defined as integrals." );
static assert( dimension >= 0,
"Dimensions sizes cannot be negative." );
}
templateTupleParameterData.dimensions = dimensionsTuple.stringof;
return templateTupleParameterData;
}
}
static bool areTemplateParametersValid( T_scalar, T_args ... )()
{
static assert( isNumeric!( T_scalar ),
"The 'T_scalar' template argument is not a numeric type." );
static assert( T_args.length > 0,
"No dimensions have been defined." );
return true;
}
Since I've just started with D, and since I'm not so sure about this hack, I would like to know if this sounds good to you guys or if there's maybe a better way of handling this?
Like you say, it's a hack, and you should avoid hacks where unnecessary.
One (obvious) solution is to move the storage order before the dimensions, although I'm guessing you want to use that default parameter.
To work around that, you could create have specific templates for row and column major:
// Generic Tensor with storage order before dimensions.
class Tensor( T_scalar, int T_storageOrder, T_dimensions... )
{
}
template TensorRowOrder( T_scalar, T_dimensions... )
{
alias Tensor( T_scalar, StorageOrder.rowMajor, T_dimensions ) TensorRowOrder;
}
template TensorColumnOrder( T_scalar, T_dimensions... )
{
alias Tensor( T_scalar, StorageOrder.columnMajor, T_dimensions ) TensorColumnOrder;
}
You can then use TensorRowOrder or TensorColumnOrder in user code, or just Tensor when you need the generic T_storageOrder.
FYI this is what I ended up doing.
class Array( T_scalar, T_args ... )
{
private:
alias ArrayTraits!( T_scalar, T_args ) traits;
alias traits.isDynamic T_isDynamic;
alias traits.shapeAtCompileTime T_shapeAtCompileTime;
alias traits.sizeAtCompileTime T_sizeAtCompileTime;
alias traits.storageOrder T_storageOrder;
alias traits.dataType T_dataType;
}
struct ArrayTraits( T_scalar, T_args ... )
if ( areTemplateParametersValid!( T_scalar, T_args )() )
{
private:
static if ( hasFlag( Flags.storageOrder ) )
alias T_args[0 .. $ - 1] shapeTuple;
else
alias T_args shapeTuple;
public:
static immutable bool isDynamic = hasFlag( Flags.dynamic ) ? true : false;
static immutable auto shapeAtCompileTime = getShapeAtCompileTime();
static immutable size_t sizeAtCompileTime = getSizeAtCompileTime();
static immutable StorageOrder storageOrder = hasFlag( Flags.storageOrder ) ?
T_args[$ - 1] : defaultStorageOrder;
static if ( hasFlag( Flags.dynamic ) == true )
alias T_scalar[] dataType;
else
alias T_scalar[sizeAtCompileTime] dataType;
public:
static auto getShapeAtCompileTime()
{
static if ( hasFlag( Flags.dynamic ) == true )
{
static assert( shapeTuple.length == 1,
"The shape of a dynamic array needs to be defined at run-time." );
size_t[1] shapeAtCompileTime = [Storage.dynamic];
return shapeAtCompileTime;
}
else
{
static assert( shapeTuple.length > 0,
"No dimensions have been defined." );
size_t[shapeTuple.length] shapeAtCompileTime;
foreach ( i, dimension; shapeTuple )
{
static assert( isIntegral!( typeof( dimension ) ),
"Dimensions sizes for a static array needs to be defined as integrals." );
static assert( dimension > 0,
"Dimensions sizes for a static array cannot be null or negative." );
shapeAtCompileTime[i] = dimension;
}
return shapeAtCompileTime;
}
}
static size_t getSizeAtCompileTime()
{
if ( hasFlag( Flags.dynamic ) == true )
return 0;
size_t size = 1;
foreach ( dimension; shapeAtCompileTime )
size *= dimension;
return size;
}
private:
/++ Parses the template tuple parameter to extract the different flags passed, if any. +/
static int getFlags()
{
int flags = 0;
if ( is( typeof( T_args[0] ) == Storage ) && T_args[0] == Storage.dynamic )
flags |= Flags.dynamic;
if ( is( typeof( T_args[$ - 1] ) == StorageOrder ) )
flags |= Flags.storageOrder;
return flags;
}
/++ Checks if the template tuple parameter contains a specific flag. +/
static bool hasFlag( Flags flag )
{
return (getFlags() & flag) == 0 ? false : true;
}
private:
enum Flags : int
{
dynamic = 1 << 0,
storageOrder = 1 << 1
}
}
bool areTemplateParametersValid( T_scalar, T_args ... )()
{
static assert( T_args.length > 0,
"No dimensions have been defined." );
return true;
}
I have read somewhere that overloading type coercion operators is risky. And I'm having some segfaults here and there. So, have I upset the hornets' nest? Here is my code:
template <typename Wee>
struct xl_type_proxy_t {
static_assert(
boost::mpl::or_<
boost::is_same< Wee, WORD > ,
boost::is_same< Wee, DWORD >
>::value, "xl_type_proxy: should instantiate correctly" );
Wee* where_to_drop;
xl_type_proxy_t( Wee* wtd):
where_to_drop( wtd )
{}
operator Wee() const {
// Return stuff with state bits cleared
//return xltypeNil;
return (*where_to_drop) & 0xAFFF;
}
xl_type_proxy_t& operator=( Wee w )
{
// Save only the flags that were already in
// 'where_to_drop'.
(*where_to_drop) = (*where_to_drop) & 0x5000;
// And now over-write the rest
(*where_to_drop) = (*where_to_drop) | w;
return *this;
}
};
...
xl_type_proxy_t< decltype(this->xl) > proxy_xl()
{
return xl_type_proxy_t<decltype(this->xl)>( &(this->xl));
}
... // In some places:
proxy_xl() = ... // Some numeric constant
... // In some others:
if ( proxy_xl() == /* some numeric constant */ )
{
...
}