How do you return multiple instances of a struct in Solidity? [duplicate] - struct

This question already has answers here:
How can I return an array of struct in solidity?
(3 answers)
Closed 1 year ago.
I'm learning about the struct data type and I want to know if:
it's possible to return multiple instances of a struct object at the same time.
an instance declared locally (inside a function) can return its value.
I want to get the details of all the instances (book1, book2, and book3) returned to me at the SAME TIME. I ran the code and could only get the details for ONE INSTANCE at A GIVEN TIME. So, the two instances I declared at state-level (book1 and book2) returned values WHEN CALLED SEPARATELY. However, book3, which was declared locally, didn't return its value when called. What I got was a declaration error (DeclarationError: Undeclared Identifier).
How do I return all the values together?
Why is book3 not returning its value? Can't a struct have a local instance?
pragma solidity >=0.4.0 <0.9.0;
// Creating a struct contract
contract MyStruct {
// Declaring a struct
struct Book {
string name;
string writer;
uint id;
bool available;
}
// Declaring a structure object, book1 (state variable of type struct; no values)
Book book1;
// Assigning values to fields for another struct instance, book2
Book book2 = Book ("Building Ethereum DApps", "Roberto Infante", 2, false);
// Defining a function to set values for the fields for structures
function setBookDetails() public {
// Assigning values to book 1 (state struct above)
book1 = Book("Introducing Ethereum and Solidity", "Chris Dannen", 1, true);
// Defining a new book instance locally, book3 (local struct)
Book memory book3 = Book ("Solidity Programming Essentials", "Ritesh Modi", 3, true);
}
// Defining function to print book1 details
function getBookDetails() public view returns (string memory, string memory, uint, bool) {
return (book1.name, book1.writer, book1.id, book1.available);
//return (book2.name, book2.writer, book2.id, book2.available);
//return (book3.name, book3.writer, book3.id, book3.available);
}
}

You can return ALL book objects with return statement if you would use array. ALSO Solidity creates getter functions automatically for public variables. As your array could be public, a public getter function would be generated for it automatically. The getter function is used to access the array variables directly and not for retrieving the array itself.
There is a difference between Storage and Memory variables. The Solidity Smart Contract can use any amount of memory during the execution but once the execution stops, the Memory is completely wiped off for the next execution. Whereas Storage on the other hand is persistent, each execution of the Smart contract has access to the data previously stored on the storage area.

I just wanted to add to #Peteris' answer by saying that it is also possible to return multiple instances of a struct without creating an array. A function can have multiple return values but you still use a single return in that case:
function getBookDetails()
public
view
returns (Book memory, Book memory, Book memory)
{
return (book1, book2, book3);
}
Of course, since book3 is only visible in setBookDetails, it will only work if you make it available to the function, e.g. by making it a storage variable, moving the declaration to the function or passing it in via a parameter.

Related

NPE when retrieving from HashMap even though containsKey() returns true in multithreaded environment

We are trying to store unique object for a particular key. When getMyObject is called in multithreaded environment we are getting null ptr exception at the time of return statement
object SampleClass
{
fun getMyObject(Id : String) : MyObject
{
if(!myMap.containsKey(Id))
{
synchronized(SampleClass)
{
if(!myMap.containsKey(Id))
{
myMap[Id] = MyObject()
}
}
}
return myMap[Id]!!
}
private val myMap = HashMap<String,MyObject>()
}
It seems even though contains method returns true when we try to get the value the value is returned null.
I am not sure what is the reason behind it.
This is the kind of trouble you get into if you try to outsmart the memory model. If you look at HashMap's source, you'll find containsKey implemented as:
public boolean containsKey(Object key) {
return getNode(key) != null;
}
Note that it returns true only if there's a HashMap.Node object corresponding to the given key. Now, this is how get is implemented:
public V get(Object key) {
Node<K,V> e;
return (e = getNode(key)) == null ? null : e.value;
}
What you're seeing is an instance of the unsafe publication problem. Let's say 2 threads (A & B) call getMyObject for a non-existent key. A is slightly ahead of B, so it gets into the synchronized block before B calls containsKey. Particularly, A calls put before B calls containsKey. The call to put creates a new Node object and puts it into the hash map's internal data structures.
Now, consider the case when B calls containsKey before A exists the synchronized block. B might see the Node object put by A, in such case containsKey returns true. At this point, however, the node is unsafely published, because it is accessed by B concurrently in a non-synchronized manner. There's no guarantee its constructor (the one setting its value field) has been called. Even if it was called, there's no guarantee the value reference (or any references set by the constructor) is published along with the node reference. This means B can see an incomplete node: the node reference but not its value or any of its fields. When B proceeds to get, it reads null as the value of the unsafely published node. Hence the NullPointerException.
Here's an ad-hoc diagram for visualizing this:
Thread A Thread B
- Enter the synchronized block
- Call hashMap.put(...)
- Insert a new Node
- See the newly inserted (but not yet
initialized from the perspective of B)
Node in HashMap.containsKey
- Return node.value (still null)
from HashMap.get
- !! throws a `NullPointerException`
...
- Exit the synchronized block
(now the node is safely published)
The above is just one scenario where things can go wrong (see comments). To avoid such hazards, either use a ConcurrentHashMap (e.g. map.computeIfAbsent(key, key -> new MyObject())) or never access your HashMap concurrently outside of a synchronized block.

Threads with same argument objects give different values

I have a problem where two threads with different functions and same argument objects result in giving different values for those objects.
To clearify, please observe the following code:
class Player(){
// Definition of Player here
// with get- and set functions
// for a certain value.
}
class Game(){
static void Draw(Player p){
while(1){
gotoxy(p.getValue(), 15);
cout << p.name();
}
}
static void Move(Player p){
int x = p.getValue();
while(1){
if(_kbhit()){
p.setValue(++x);
}
}
}
void startGame(){
Player pl1(5);
thread thd1(Move, pl1);
thread thd2(Draw, pl1);
thd1.join();
thd2.join();
}
}
While the value 'x' is changing in the function 'Move' for every key stroke, when getting that value in function 'Draw' still has the initial value for 'pl1' (which is 5).
How can I get 'Draw' to aquire the same value that 'Move' has given?
I appreciate any help and guidance.
Thank you in advance!
You are passing the player by value
static void Move(Player pl)
rather than by reference/pointer, so both functions have their own, local, copies of the original variable.
static void Move(Player& pl)
will take the variable by reference and give both functions access to the original variable.
Also, unless getValue and setValue implement some form of locking, this code is not thread safe.
The problem is that you are passing pl1 by value, when you want to be passing it by reference. Even though it looks like you are passing pl1 into each function, what's really going on is that the Move and Draw threads are each constructing new Player objects. If you pass by references, then both threads will refer to the same object as opposed to creating their own copies. Try changing the signatures of the functions to the following:
static void Move(Player &p);
static void Draw(Player &p);
Also, consider putting some exit condition into your function. Since while(1) will never exit, the join() functions will wait forever. Hope that helps!

Struct store large chunk of data

I have a data file with millions of rows and I wanted to read that and store in a struct.
public struct Sample
{
public int A;
public DateTime B;
}
Sample[] sample = new Sample[];
This definition gives me this error "Wrong number of indicies inside[]; expected 1"
How do I store data in struct (with less memory usage)? Array is that best of something else?
var reader = new StreamReader(File.OpenRead(#"C:\test.csv"));
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
var values = line.Split(';');
}
In order to allocate an array, you need to specify how many elements it holds. That's what the error is telling you.
If you don't know the size, you can use List<T> which will grow as needed. Internally List<T> is implemented using T[] which means that from a look-up stand point it acts like an array. However, the work of reallocating larger arrays as needed is handled by List<T>.

C++ : Strings, Structures and Access Violation Writing Locations

I'm attempting to try and use a string input from a method and set that to a variable of a structure, which i then place in a linked list. I didn't include, all of code but I did post constructor and all that good stuff. Now the code is breaking at the lines
node->title = newTitle;
node->isbn = newISBN;
So newTitle is the string input from the method that I'm trying to set to the title variable of the Book structure of the variable node. Now, I'm assuming this has to do with a issue with pointers and trying to set data to them, but I can't figure out a fix/alternative.
Also, I tried using
strcpy(node->title, newTitle)
But that had an issue with converting the string into a list of chars because strcpy only uses a list of characters. Also tried a few other things, but none seemed to pan out, help with an explanation would be appreciated.
struct Book
{
string title;
string isbn;
struct Book * next;
};
//class LinkedList will contains a linked list of books
class LinkedList
{
private:
Book * head;
public:
LinkedList();
~LinkedList();
bool addElement(string title, string isbn);
bool removeElement(string isbn);
void printList();
};
//Constructor
//It sets head to be NULL to create an empty linked list
LinkedList::LinkedList()
{
head = NULL;
}
//Description: Adds an element to the link in alphabetical order, unless book with
same title then discards
// Returns true if added, false otherwise
bool LinkedList::addElement(string newTitle, string newISBN)
{
struct Book *temp;
struct Book *lastEntry = NULL;
temp = head;
if (temp==NULL) //If the list is empty, sets data to first entry
{
struct Book *node;
node = (Book*) malloc(sizeof(Book));
node->title = newTitle;
node->isbn = newISBN;
head = node;
}
while (temp!=NULL)
{
... //Rest of Code
Note that your Book struct is already a linked list implementation, so you don't need the LinkedList class at all, or alternatively you don't need the 'next' element of the struct.
But there's no reason from the last (long) code snippet you pasted to have an error at the lines you indicated. node->title = newTitle should copy the string in newTitle to the title field of the struct. The string object is fixed size so it's not possible to overwrite any buffer and cause a seg fault.
However, there may be memory corruption from something you do further up the code, which doesn't cause an error until later on. The thing to look for is any arrays, including char[], that you might be overfilling. Another idea is you mention you save method parameters. If you copy, it's ok, but if you do something like
char* f() {
char str[20];
strcpy(str, "hello");
return str;
}
...then you've got a problem. (Because str is allocated on the stack and you return only the pointer to a location that won't be valid after the function returns.) Method parameters are local variables.
The answer you seek can be found here.
In short: the memory malloc returns does not contain a properly constructed object, so you can't use it as such. Try using new / delete instead.

Constants in Haxe

How do you create public constants in Haxe? I just need the analog of good old const in AS3:
public class Hello
{
public static const HEY:String = "hey";
}
The usual way to declare a constant in Haxe is using the static and inline modifiers.
class Main {
public static inline var Constant = 1;
static function main() {
trace(Constant);
trace(Test.Constant);
}
}
If you have a group of related constants, it can often make sense to use an enum abstract. Values of enum abstracts are static and inline implicitly.
Note that only the basic types (Int, Float, Bool) as well as String are allowed to be inline, for others it will fail with this error:
Inline variable initialization must be a constant value
Luckily, Haxe 4 has introduced a final keyword which can be useful for such cases:
public static final Regex = ~/regex/;
However, final only prevents reassignment, it doesn't make the type immutable. So it would still be possible to add or remove values from something like static final Values = [1, 2, 3];.
For the specific case of arrays, Haxe 4 introduces haxe.ds.ReadOnlyArray which allows for "constant" lists (assuming you don't work around it using casts or reflection):
public static final Values:haxe.ds.ReadOnlyArray<Int> = [1, 2, 3];
Values = []; // Cannot access field or identifier Values for writing
Values.push(0); // haxe.ds.ReadOnlyArray<Int> has no field push
Even though this is an array-specific solution, the same approach can be applied to other types as well. ReadOnlyArray<T> is simply an abstract type that creates a read-only "view" by doing the following:
it wraps Array<T>
it uses #:forward to only expose fields that don't mutate the array, such as length and map()
it allows implicit casts from Array<T>
You can see how it's implemented here.
For non-static variables and objects, you can give them shallow constness as shown below:
public var MAX_COUNT(default, never):Int = 100;
This means you can read the value in the 'default' way but can 'never' write to it.
More info can be found http://adireddy.github.io/haxe/keywords/never-inline-keywords.

Resources