I have a function that returns int value but in some circumstances I want to terminate the function and return nothing.
Just to show an example:
int numberFunc(int num){
if(num > 10){
return;
/* terminates the function for numbers more than 10 */
}
return num;
}
This works fine but I get a warning that says the function has a return type of int but it doesn’t finish with a return statement.
Will there be a problem if I use something like this and what can be a solution for that?
Thanks
Solution:
As julemand101 explained, We can return a null value. Also the code above returns a null value for numbers more than 10 so we have to take care of the possible null values later on.
What you properly want is to return null to indicate that the method are not returning any value:
int numberFunc(int num){
if(num > 10){
return null;
/* terminates the function for numbers more than 10 */
}
return num;
}
But remember that the method which are using the result from numberFunc needs to be aware that the returned value can be null.
You can just return null. It's a default value for any type that has not been set yet.
Ex.:
int i; is the same as int i = null
Related
I am currently writing a Smart Contract in Solidity. The smart contract, amongst other information, stores an array of properties object at the general level. The property object Looks like this:
struct PropertyObj {
string id;
uint weiPrice;
address owner;
}
Now there is a specific function that iterates over the array, finds the property and returns it (code below)
function getPropertyByid(string memory _propertyId)private view returns(PropertyObj memory){
for(uint i = 0; i<PropertyArray.length; i++){
if (keccak256(bytes((PropertyArray[i].id))) == keccak256(bytes((_propertyId)))) {
return PropertyArray[i];
}
return null;
}
}
The "Problem" is that, unlike other programming languages, Solidity does not allow to return null (as far as I am concerned).
In other words, if throughout the iteration we do not find the property, then what we shall return if we specified that we need to return PropertyObj memory in the function signature?
Solidity does not have null value, as you're correctly stating.
Your function can throw an exception using the revert() function.
It also seems that your implementation has a logical error. Your example would "return null" if the hash was not found during the first iteration. Instead, you may want to throw the exception after the loop has ended.
for(uint i = 0; i<PropertyArray.length; i++){
if (keccak256(bytes((PropertyArray[i].id))) == keccak256(bytes((_propertyId)))) {
return PropertyArray[i];
}
}
revert('Not found');
Other option would be to return the empty object (with default values, i.e. zeros), if it fits your use case.
for(uint i = 0; i<PropertyArray.length; i++) {
// ...
}
// not found, return empty `PropertyObj`
PropertyObj memory emptyPropertyObj;
return emptyPropertyObj;
I would like to express something like this in Critcl:
void setter(int* grid, int value, int x, int y) {
grid[xy2addr(x,y)] = value;
}
I'm in particular stuck on how to deal with int* grid in Critcl. object? bytes? Custom type maybe?
Related to this question.
This case doesn't map very well onto Tcl's value model. The issue is that grid is (a pointer to) an updateable value collection. There are two ways of modelling this in Tcl in general:
As an opaque object.
As a variable containing a Tcl list (since in model terms, while Tcl values are thought of as immutable, Tcl variables are mutable).
I'll describe how to do both below, but I'm guessing that you're going to be thinking of these zOrder things as a distinct mutable type and that the additional modest one-time overhead of making the custom type will suit you far better.
Opaque (Mutable) Objects
When working with opaque objects, you pass handles to them (basically just a name) around and then you unpack them as a custom Critcl type. The trick is to create some helper functions in C to do the mapping (this can be in a critcl::ccode command) that does the mapping between names and pointers. This is slightly messy to do, but is just about building a couple of hash tables.
critcl::ccode {
static Tcl_HashTable *zOrderMap = NULL, *zOrderRevMap = NULL;
static Tcl_Obj *
MakeZOrderObj(int *zOrder) {
/* Initialize the two maps, if needed */
if (zOrderMap == NULL) {
zOrderMap = (Tcl_HashTable *) Tcl_Alloc(sizeof(Tcl_HashTable));
Tcl_InitObjHashTable(zOrderMap);
zOrderRevMap = (Tcl_HashTable *) Tcl_Alloc(sizeof(Tcl_HashTable));
Tcl_InitHashTable(zOrderRevMap, TCL_ONE_WORD_KEYS);
}
int isNew;
Tcl_HashEntry *hPtr = Tcl_FindHashEntry(zOrderRevMap, (char*) zOrder, &isNew);
if (!isNew) {
return Tcl_GetHashValue(hPtr);
}
/* make a handle! */
Tcl_Obj *handle = Tcl_ObjPrintf("zOrder%ld", (long) zOrder);
Tcl_SetHashValue(hPtr, handle);
Tcl_IncrRefCount(handle);
hPtr = Tcl_CreateHashEntry(zOrderMap, (char*) handle, &isNew);
Tcl_SetHashValue(hPtr, zOrder);
return handle;
}
static int
GetZOrderFromObj(Tcl_Interp *interp, Tcl_Obj *objPtr, int **zOrderPtr) {
Tcl_HashTable *hPtr;
if (!zOrderMap || (hPtr = Tcl_FindHashEntry(zOrderMap, (char *) objPtr)) == NULL) {
Tcl_SetObjResult(interp, Tcl_ObjPrintf("no such zOrder \"%s\"",
Tcl_GetString(objPtr)));
return TCL_ERROR;
}
*zOrderPtr = (int *) Tcl_GetHashValue(hPtr);
return TCL_OK;
}
}
With that helper code in place, you can then define a custom Critcl type like this:
critcl::argtype zOrder {
if (GetZOrderFromObj(interp, ##, #A) != TCL_OK) {
return TCL_ERROR;
}
} int*
critcl::resulttype zOrder {
if (rv == NULL) {
return TCL_ERROR;
}
Tcl_SetObjResult(interp, MakeZOrderObj(rv));
return TCL_OK;
} int*
That then lets you write your real code as something like this. Note that grid is defined as being of (custom) type zOrder, and that those can only be manufactured by some code that returns a zOrder as its result.
critcl::cproc setter {zOrder grid int value int x int y} void {
grid[xy2addr(x,y)] = value;
}
(The deletion function that removes the entries from the hash tables and deletes the C array is left as an exercise.)
Tcl List Variable
The other way of doing this is to make zOrder values be held in Tcl variables as lists of integers. This can be nice because it lets you look inside easily, but it can also be not so nice in other ways, as the code is not constrained to work with proper values and you expose your cprocs to more details of what's happening in Tcl.
critcl::cproc setter {Tcl_Interp* interp object varName int value int x int y} ok {
/* Unpack the list of ints from the variable */
Tcl_Obj *listObj = Tcl_ObjGetVar2(interp, varName, NULL, TCL_LEAVE_ERR_MSG);
if (listObj == NULL)
return TCL_ERROR;
Tcl_Obj **listv; int listc;
if (Tcl_ListObjGetElements(interp, listObj, &listc, &listv) != TCL_OK)
return TCL_ERROR;
int *grid = alloca(sizeof(int) * listc);
for (int i=0; i<listc; i++)
if (Tcl_GetIntFromObj(interp, listv[i], &grid[i]) != TCL_OK)
return TCL_ERROR;
/* The core of the functionality */
grid[xy2addr(x,y)] = value;
/* Repack the list of ints from the variable; this code could be optimized in this case! */
for (int i=0; i<listc; i++)
listv[i] = Tcl_NewIntObj(grid[i]);
listObj = Tcl_NewListObj(listc, listv);
Tcl_ObjSetVar2(interp, varName, NULL, listObj, 0);
return TCL_OK;
}
struct buyer{
uint amount;
Status status;
}
mapping(address=>buyer) public buyers;
mapping(uint=>address) buyerIndex;
uint public buyerNum;
//Order a product.
function(){
uint doubleValue=value*2;
uint amount=msg.value/doubleValue;
if(buyers[msg.sender]==null){ //Error in this line
buyer abuyer=buyer({amount:amount,status:Status.Created}); //Error in this line
buyerNum++;
buyerIndex[buyerNum]=msg.sender;
buyers[msg.sender]=abuyer;
}else{
buyers[msg.sender].amount+=amount;
}
Order(msg.sender,amount*doubleValue,amount);
}
If a buyer is not recorded in the buyer mapping, then buyerNum++;
but I don't know how to tell whether a buyer is in the mapping
In solidity every variable is set to 0 by default.
You should think of mappings as all possible combinations are set to 0 by default.
In your specific case I would use the following:
if (buyers[msg.sender].amount == 0)
For integers:
You could create none variable to use it as a NULL:
uint256 constant NULL = 0;
Example code for check:
function isNULL(uint256 variable) internal returns (bool) {
return variable == NULL;
}
For bytes32:
You can follow different approach for bytes:
bytes32 constant NULL = "";
Example code piece:
pragma solidity ^0.6.0;
mapping(address => bytes32) public Countries;
function isCountriesInitialized(address _user) external view returns (bool)
{
if (Countries[_user] == NULL) // Returns true if `Countries[_user]` is not initialized
return false;
return true;
}
I observe that on solidity >= v0.6.0 it may return 32 for length even though it is not mapped.
Example of its returned value:
b'\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00'
As Viktor said, default value for all possible values in mapping is zero. So if a buyer has not already inserted in mapping, the amount value for that address will be zero. But this approach has a flaw, if a buyer does exists but its balance became zero after some operations, you will treat it as it does not exist.
I think the best approach is to add a exists member to the buyer struct with bool type. Default value for this member is false and when the buyer get created, you initialize it with true value. So you can check exactly if a buyer exist or not via this member.
Buyer struct:
struct buyer{
uint amount;
Status status;
bool exists;
}
Initialize buyer:
buyer memory b = buyer(0, status, true);
Check if buyer exists:
if(buyers[msg.sender].exists) {
//so can buy
}
There is nothing like null in solidity.
Just check for the length of the address:
if(buyers[msg.sender].length == 0){
// do your thing
}
See also this answer on ethereum stack exchange.
Instead of using one of the values or creating an extra boolean, you can check for the byte size of the structure.
if (bytes(buyers[msg.sender]).length > 0) {
// buyer exists.
}
Just wondering, if i could provide default argument for function in ActionScript 3 which is never can be passed to the function by user. This is the case:
public function getAttr (obj:Object, key:String, def:* = DEFAULT_VALUE):* {
if (!obj.hasOwnProperty(key)) {
if (def === DEFAULT_VALUE) {
throw ReferenceError('Attribute not found: ' + key);
} else {
return def;
}
} else {
return obj[key];
}
}
I cannot use as DEFAULT_VALUE any of null, undefined, Number, Boolean or String here, cause, logically, user could use any of this values. I need something really unique here. In python, for example, i can do this:
_DEFAULT_VALUE = object()
def get_attr(obj, key, d=_DEFAULT_VALUE):
if not hasattr(obj, key):
if d is DEFAULT_VALUE:
raise KeyError('Attribute not found: {}'.format(key))
else:
return d
else:
return obj[d]
But in ActionScript 3 such approach produces an error:
Error code: 1047: Parameter initializer unknown or is not a compile-time constant.
Maybe some hack here?
Your problem here is that def value have to be a compile time constant so there is no way to store a default value into a var and pass it as a default value.
But what you really want here is to know if the user have passed an extra parameter into the def field of the function, so you can check the arguments array length and see if there is 2 or 3 parameters passed.
public function getAttr (obj:Object, key:String, def:* = null):* {
if (!obj.hasOwnProperty(key)) {
if (arguments.length==2) { // no default value passed to the function
throw new ReferenceError('Attribute not found: ' + key);
} else {
return def;
}
} else {
return obj[key];
}
}
Transfer null, and check for null. After all, null is a default value that's really nothing, if you require a parameter to be supplied and be valid, use null as default value so that if something will be supplied, it will not be null, thus valid.
Vesper's answer seems correct for your situation. But, I've used similar method for singletons though:
class Singleton
{
private var k_CONS_BLOCKER:Object = {};
public Singleton(cons_blocker:Object)
{
if(cons_blocker!=k_CONS_BLOCKER)
{
throw new Error("Use static getter to get the object");
}
}
public function getObject():Singleton
{
return obj;//you can of course create it now or do some other logic...
}
}
I know the answer isn't directly related to the question, but I think the idea solves the problem in general.
HTH.
In ActionScript 3, is there a clean way to define a function that accepts an optional boolean argument ? As you may know, this is invalid :
public function test(param:Boolean = null):void {
trace(param);
}
This triggers the following error: VerifyError: Error #1102: Illegal default value for type Boolean. Since, Boolean is a primitive, I guess it makes sense that it cannot be set to null. The only workaround I found is to cast the parameter to an object :
public function test(param:Object = null):void {
trace(Boolean(param));
}
However, this does not feel very clean, particularly if you are developing libraries. ASDoc will generate API documentation that says the expected parameter is an Object whereas what is really needed is a Boolean.
Is there a better approach ?
When you say optional, I assume that you mean if there isn't a value supplied then something different should happen compared to if you had a default value of true or false.
You could make your own object to handle the three states that you need and maintain code readability by using a class like this:
public class Condition
{
private var _value:* = null;
public function Condition(initial:* = null)
{
value = initial;
}
public function set value(n:*):void
{
if(_value === null || _value === false || _value === true)
{
_value = n;
}
}
public function get value():*{ return _value; }
}
And then your function could be:
function test(param:Condition = null):void
{
if(param && param.value != null)
{
trace(param.value);
}
}
test( new Condition() );
test( new Condition(true) );
As you said Boolean can not be set to null value.
Therefore, you should specify a default value that is either true or false.
public function test(param:Boolean = false):void {
trace(param);
}
But because you need the third case where nothing is set, one option could be to accept any Object but throw an exception if it is not null and not a boolean:
public function test(param:* = null):void
{
if (param != null)
{
if ((param == true) || (param == false))
{
trace(Boolean(param).toString());
}
else
{
throw new CustomError("param should be a boolean");
}
}
else
{
// Do nothing
}
}
Note that this solution also accept objects or primitives that can be compared to true or false such as 0, 1, or [].
From the good suggestions and discussion above I think that, in a library scenario and for simplicity's sake, the best way remains to type the parameter as Object with a default value of null but to request a Boolean in the API documentation :
/**
* #param param Boolean object or null
*/
public function test(param:Object = null):void {
trace(Boolean(param));
}
This allow the user of the library to pass a either a Boolean or nothing at all. Thanks everyone.
There was a tonne of discussion on my previous answer, but this is the correct way to have a function that accepts one of three states. My previous answer attempted to retain the use of a Boolean value like you were requesting, but that is not the right way to go about it.
Create a class that defines three values:
class State
{
public static const EMPTY:int = -1;
public static const FALSE:int = 0;
public static const TRUE:int = 1;
}
Your function will accept an int (the type of each of the three properties within your State class). It will deal with the three possible values. You can use concise commenting to notify the developer of what thee values the function is expecting, referencing the State class. The default value can be -1 aka State.EMPTY.
/**
* Function description.
* #param state One of three states, defined by State.
*/
function test(state:int = -1):void
{
switch(state)
{
case State.EMPTY:
// No value given.
break;
case State.TRUE:
// True.
//
break;
case State.FALSE:
// False.
//
break;
default:
throw new ArgumentError("Unsupported value for test()");
break;
}
}