By returning a struct from a function, how can I verify that it is initialized?

I have the following structure in C ++:

struct routing_entry { unsigned long destSeq; // 32 bits unsigned long nextHop // 32 bits unsigned char hopCount; // 8 bits }; 

And I have the following function:

 routing_entry Cnode_router_aodv::consultTable(unsigned int destinationID ) { routing_entry route; if ( routing_table.find(destinationID) != routing_table.end() ) route = routing_table[destinationID]; return route; // will be "empty" if not found } 

"routing_table" is stl :: map, defined as follows:

 map< unsigned long int, routing_entry > routing_table; 

Now my question is, when using the consultTable function, I want to check that the return value is actually initialized, some like, for example, in the Java pseudocode (because I came from the Java camp):

 Route consultTable(int id) { Route r = table.find(id); return r; } 

then checks if r == null

+4
source share
9 answers

There are several issues here. The most urgent may be what happens when the destination identifier is not found. Since you do not have a constructor for routing_entry , and you are not initialized by default, it will have undefined values.

 // the data inside route is undefined at this point routing_entry route; 

One way to handle this is to initialize by default. This works by instructing the compiler to populate the structure with zeros. This is a kind of trick borrowed from C, but it works well here.

 routing_entry route={0}; 

You mentioned that you come with Java, unlike Java, the structure and members of the class are not initialized to 0, so you have to deal with this somehow. Another way is to define a constructor:

 struct routing_entry { routing_entry() : destSeq(0) , nextHop(0) , hopCount(0) { } unsigned long destSeq; // 32 bits unsigned long nextHop; // 32 bits unsigned char hopCount; // 8 bits }; 

Also note that in C ++, the size of an integer and char element is not defined in bits. The char type is 1 byte (but no byte is defined, but usually 8 bits). Lengths are usually 4 bytes these days, but there may be some other values.

Switching to consultTable with initialization fix:

 routing_entry Cnode_router_aodv::consultTable(unsigned int destinationID ) { routing_entry route={0}; if ( routing_table.find(destinationID) != routing_table.end() ) route = routing_table[destinationID]; return route; // will be "empty" if not found } 

One way to say, maybe, is to check if the whole structure is still zeroed. I prefer refactoring to return a bool function to indicate success. In addition, I always typed STL structures for simplicity, so I will do it here:

 typedef map< unsigned long int, routing_entry > RoutingTable; RoutingTable routing_table; 

Then we pass the link to the routing entry to populate. It may be more efficient for the compiler, but it probably doesn't matter - in any case, this is just one method.

 bool Cnode_router_aodv::consultTable(unsigned int destinationID, routing_entry &entry) { RoutingTable::const_iterator iter=routing_table.find(destinationID); if (iter==routing_table.end()) return false; entry=iter->second; return true; } 

You would call it like this:

 routing_entry entry={0}; if (consultTable(id, entry)) { // do something with entry } 
+15
source

The best way I found for this is to use boost :: optional , which is designed to solve this very problem.

Your function will look something like this: -

 boost::optional<routing_entry> consultTable(unsigned int destinationID ) { if ( routing_table.find(destinationID) != routing_table.end() ) return routing_table[destinationID]; else return boost::optional<routing_entry>() } 

And your calling code looks like

 boost::optional<routing_entry> route = consultTable(42); if (route) doSomethingWith(route.get()) else report("consultTable failed to locate 42"); 

As a rule, the use of the "out" parameters (passing a pointer or a reference to an object that is then "populated" by the called function is not trusted in C ++. The approach that everything "returns" by the function is contained in the returned value and that no functional parameters are changed, can make code more readable and maintainable in the long run.

+3
source

This is a typical solution to your problem:

 bool Cnode_router_aodv::consultTable(unsigned int destinationID, routing_entry* route ) { if ( routing_table.find(destinationID) != routing_table.end() ) { *route = routing_table[destinationID]; return true; } return false; } 

Instead of a pointer, you can use a link; it is a matter of style.

+2
source

First of all, note that in C ++, unlike Java, users can define value types. This means that there are 2 ^ 32 * 2 ^ 32 * 2 ^ 8 possible values โ€‹โ€‹for routing_entry. If you want, you can think of routing_entry as a 72-bit primitive type, although you should be a little careful with the analogy.

Thus, in Java route can be NULL, and there are 2 ^ 32 * 2 ^ 32 * 2 ^ 8 + 1 different values โ€‹โ€‹are useful for the variable a routing_entry . In C ++, it cannot be null. In Java, โ€œemptyโ€ can mean returning a null reference. In C ++, only pointers can be empty, and routing_entry not a pointer type. Therefore, in your code, in this case, โ€œemptyโ€ means โ€œI donโ€™t know how important this thing is because I never initialized it or assigned it to itโ€.

In Java, the routing_entry object will be allocated on the heap. In C ++, you do not want to do this if you do not need it, because memory management in C ++ requires effort.

You have a few (good) options:

1) add a field to the routing record to indicate that it has been initialized. Most likely, this will not make the structure larger due to the requirements of complementing and aligning your implementation:

 struct routing_entry { unsigned long destSeq; // 32 bits on Win32. Could be different. unsigned long nextHop // 32 bits on Win32. Could be different. unsigned char hopCount; // 8 bits on all modern CPUs. Could be different. unsigned char initialized; // ditto }; 

Why not use bool? Because the standard helps sizeof(bool) != 1 . It is possible that bool is implemented as an int, especially if you have an old C ++ compiler. This will make your structure larger.

Then make sure that the structure is included at 0 in your function, and not in the garbage on the stack:

 routing_entry Cnode_router_aodv::consultTable(unsigned int destinationID ) { routing_entry route = {}; if ( routing_table.find(destinationID) != routing_table.end() ) route = routing_table[destinationID]; return route; // will be "empty" if not found } 

And make sure that all participants on the map have an initialized field other than zero. Then the caller checks the initialization.

2) Use the "magic" values โ€‹โ€‹of existing fields as markers.

Suppose, for the sake of argument, that you never do routes with hopCount 0. Then, as long as you do the 0-initialization, as mentioned above, callers can check hopCount! = 0. The maximum type values โ€‹โ€‹are also good flag values โ€‹โ€‹- since you limit your routes to 256 jumps, most likely you will not do any harm by limiting them to 255 jumps. Instead of callers having to remember this, add a method to the struct:

 struct routing_entry { unsigned long destSeq; // 32 bits unsigned long nextHop // 32 bits unsigned char hopCount; // 8 bits bool routeFound() { return hopCount != (unsigned char)-1; } }; 

Then you will initialize as follows:

 routing_entry route = {0, 0, -1}; 

or if you are worried about what will happen when you change the order or number of fields in the future:

 routing_entry route = {0}; route.hopCount = -1; 

And the caller:

 routing_entry myroute = consultTable(destID); if (myroute.routeFound()) { // get on with it } else { // destination unreachable. Look somewhere else. } 

3) The subscriber goes to routing_entry by pointer or non-const reference. Callee fills the response to this and returns a value indicating whether it succeeded or not. This is usually called "out param" because it kind of mimics a function that returns routing_entry and bool.

 bool consultTable(unsigned int destinationID, routing_entry &route) { if ( routing_table.find(destinationID) != routing_table.end() ) { route = routing_table[destinationID]; return true; } return false; } 

Caller Call:

 routing_entry route; if (consultTable(destID, route)) { // route found } else { // destination unreachable } 

By the way, when using a card, your code when it searches for an identifier twice. You can avoid this as follows, although this is unlikely to significantly affect the performance of your application:

 map< unsigned long int, routing_entry >::iterator it = routing_table.find(destinationID); if (it != routing_table.end()) route = *it; 
+1
source

Another way is to return the function to the status value (HRESULT or the like), indicating whether it was initialized, and pass a pointer to the structure as one of the parameters.

In C ++, a status is usually returned indicating the error code (or 0 if successful), but this, of course, depends on your programming habits.

Just passing the pointer and checking for zero will work anyway.

0
source

As an alternative to solving the input parameter, you can follow Uncle Bob's advice and create a class for reading records.

 typedef map< unsigned long int, routing_entry > routing_table_type; routing_table_type routing_table; //Is valid as long as the entry is not removed from the map class routing_entry_reader { const routing_table_type::const_iterator routing_table_entry; const routing_table_type& routing_table; public: routing_entry_reader( const routing_table_type& routing_table, int destination_id ) : routing_table(routing_table), routing_table_entry( routing_table.find(destination_id) ) { } bool contains_entry() const { return routing_table_entry!=routing_table.end(); } const routing_entry& entryByRef() const { assert(contains_entry()); return routing_table_entry->second; } }; routing_entry_reader entry_reader(routing_table, destination_id); if( entry_reader.contains_entry() ) { // read the values from the entry } 
0
source
 shared_ptr<routing_entry> Cnode_router_aodv::consultTable(unsigned int destinationID ) { shared_ptr<routing_entry> route; if ( routing_table.find(destinationID) != routing_table.end() ) route.reset( new routing_entry( routing_table[destinationID] ) ); return route; // will be "empty" if not found } // using void Cnode_router_aodv::test() { shared_ptr<routing_entry> r = consultTable( some_value ); if ( r != 0 ) { // do something with r } // r will be freed automatically when leaving the scope. } 
0
source

G'day

Agreeing with most 1800 words, I would be more inclined to have your consultTable function return a pointer to the routing_entry structure, rather than a boolean.

If the entry is found in the table, the function returns a pointer to the new routing_entry. If it is not found, it returns NULL.

BTW Good answer, 1800.

NTN

amuses

0
source

In your method

 routing_entry Cnode_router_aodv::consultTable(unsigned int destinationID ) { routing_entry route; ... return route; } 

You are trying to return automatic, i.e. the object is in the local stack, the object. This will never do what you want, as this memory is not available when the function goes out of scope.

You will need to create an object, and then return the newly created object. I suggest you turn to Scott Meyers Effective C ++ Third Edition, Item # 21.

0
source

Source: https://habr.com/ru/post/1286459/


All Articles