Avoid invalid data in structure mapping

Ingo Foerster
Ingo Foerster used Ask the Experts™
on
I have in my API a value mapping:

cParams.nVerifyBufferSize = (int32)Params->VerifyBufferSize;

Open in new window


How is the best way to make sure that there is a valid value given? Like
Params->VerifyBufferSize

Open in new window

is possible invalid (nothing) I want to give 1 if not a valid value.
Any suggestion?  Wiath a boolean value I use

Params->FullCapacity ? BS_TRUE : BS_FALSE;

Open in new window


I think is a  goodsolution, isn't it? Something similar for VerifyBufferSize is needed

I use this inside a dotNet component.
Comment
Watch Question

Do more with

Expert Office
EXPERT OFFICE® is a registered trademark of EXPERTS EXCHANGE®
Hello Ingo,

allow me to start with some questions.

what exactly is invalid for you?
What kind of data type is VerifyBufferSize?
You have checked that Params is not and handled if it is?

Depending on the source data type, you have to handle the mapping.

For your boolean example. The source is bool data type? What is BS_TRUE and BS_FALSE

leflon
Ingo FoersterProject Leader

Author

Commented:
The structure inside the component is:

	public ref struct CreateImageParams
	{
		String				^ImagePath;
		String				^BadSectorsFilePath;
		ImageFormat			ImageType;
		int					VerifyBufferSize;
		bool				FullCapacity;
		ReadErrorCorrectionParams	^ErrorParams;
	};

Open in new window


I have to map them to a C++ structure inside a third party library.

BS_TRUE / BS_FALSE are int8 values.  

Not valid means, not set. Like as sample someone initilaize the structure but not initialize the value VerifyBufferSize so it is finaly an unitialized int value.
evilrixSenior Software Engineer (Avast)

Commented:
There's no such thing as an uninitialised value in C/C++. What I mean is, there is no special value. Your options are as follows:

1. Use a pointer for each type, and set the point to nullptr if to indicate not set.

2. Make each value a std:pair and set one of either first or second as a bool to show not set.

https://en.cppreference.com/w/cpp/utility/pair

3. Use std::optional, a type design to solve this very problem. Introduced in C++17. If you're using older C++ the Boost project has a facsimile.

https://en.cppreference.com/w/cpp/utility/optional
CompTIA Security+

Learn the essential functions of CompTIA Security+, which establishes the core knowledge required of any cybersecurity role and leads professionals into intermediate-level cybersecurity jobs.

Fabrice LambertConsulting
Distinguished Expert 2017

Commented:
As last resort, you can write a constructor for your structure, that initialise members default values.
Then, after filling up the structure, if a member still has its default value, it can mean something went wrong.
In all cases, you'll have to write some validation code.

Side note:
cParams.nVerifyBufferSize = (int32)Params->VerifyBufferSize;

Open in new window

This kind of cast is deprecated in C++, use static_cast instead.
evilrixSenior Software Engineer (Avast)

Commented:
>> This kind of cast is deprecated in C++, use static_cast instead

C style casts are not deprecated, they are just different and serve a different purpose. In fact, if they were deprecated it would break a lot of valid C++ code. C++11 actually augmented the available syntax for this type of casting.

https://en.cppreference.com/w/cpp/language/explicit_cast

>> if a member still has its default value, it can mean something went wrong.

The problem is that if a valid value just happens to be the default value, you have no way to discern the difference, which is why you need to resort to one of the methods I noted above. There ar other methods, but those are the simplest.
Fabrice LambertConsulting
Distinguished Expert 2017

Commented:
Well,

Like many languages, C++ still has a lot of old features who's sole purpose is backward compatibility.
Modern codes should avoid these, as much as possible.
evilrixSenior Software Engineer (Avast)

Commented:
>> who's sole purpose is backward compatibility
Except in this case, it's existence isn't just for backward compatibility.The same semantics are used by C++, you just may not have realised it. For example, consider constructor casting. This is just a C-style cast in disguise.

eg.

auto x = foo(1);

Is semantically identical to

auto x = (foo) 1;

As far as the compiler is concerned, they mean exactly the same thing.

auto i = int(1);

Is semantically identical to

auto i = (int) 1;

#include <iostream>

struct foo
{
	explicit foo(int const x)
	{
		std::cout << x << std::endl;
	}
};

int main()
{
	foo(1); // this is normally how we'd write things
	(foo)2; // rarely used form, but identical the the previous line
}

Open in new window


The placement of the brackets is just syntactic sugar, in much the same was as indexers on arrays are just syntactic sugar and you can swap them around:

array[1]

is the same as

1[array]

#include <iostream>

int main()
{
	auto foo = "hello";
	std::cout << 1[foo] << std::endl;
}

Open in new window

Fabrice LambertConsulting
Distinguished Expert 2017

Commented:
The more errors detected by the compiler, the better:
long x = 15;
long* y = &x;

long value = (long)y;          // C style cast, compile without error.
value = static_cast<long>(y);  // C++ style cast, does not compile at all.

Open in new window

evilrixSenior Software Engineer (Avast)

Commented:
Well, the line with the static_cast doesn't compile because that sort of cast needs reinterpret_cast, which compiles fine!

Anyway, I don't disagree that one should prefer C++ style casting, I'm just disagreeing with the point you made about C style casting being deprecated. It's not and I've tried to explain why. It's still intrinsic to the syntax of C++.
Top Expert 2016

Commented:
I don't disagree that one should prefer C++ style casting

actually I do disagree.

Fabrice, your mistaking of static_cast and reinterpret_cast shows that even experienced programmers can fail in using the right c++ style cast. when using c style cast the compiler will choose the right casting operation depending on the type of the operands what is simpler and in my opinion less error-prone than letting the programmer choose the right method.


There's no such thing as an uninitialised value in C/C++. What I mean is, there is no special value.

while this is true in general,  you properly can define 'special values' for an integer variable by defining constants or enum,

enum CompareOps
{
       CO_Invalid = -1,
       CO_Contains,
       CO_Default = CO_Contains,
       CO_Equal,
       ...
       CO_NotEqual,
       CO_NUM_OPS    // automatically counts all valid values
};
... 

X::X() : compOp(CO_Default), .... {}

bool Y::foo(int cop)
{
      if (cop == CO_Invalid)
            return false;
      ....
      return true;
}     

Open in new window


is possible invalid (nothing) I want to give 1 if not a valid value.

1 should not be used for invalid in c++ beside of the main function which returns 0 on success and (any) non-zero on fail.

c++ has bool type which is only true or false. only false (== 0 internally) could/should be both the initial value and the fail value if returned from called function. i never would use true (== 1 internally) on fail.

for integer variables mostly -1 was used on fail and on initialisation. that even works for unsigned int.

const unsigned int NIL = (unsigned int)(-1);   
    ....

    if (find(somestring, somewhat) != NIL)
    {
       return dosomething();
    }
    return NIL;      

Open in new window


Sara

Do more with

Expert Office
Submit tech questions to Ask the Experts™ at any time to receive solutions, advice, and new ideas from leading industry professionals.

Start 7-Day Free Trial