Solidity: TypeError Invalid type. Invalid implicit conversion from type (string storage pointer) to string meory requested - struct

I am trying to return values from a struct, different approaches weren`successful
struct Briefing {
string description;
uint max_influencer;
uint jobvalue;
bool achievement;
}
function getSummary() public view returns (Briefing) {
return Briefing (
string,
uint,
uint,
bool
);

When you're working with reference types (such as struct), you always need to specify the data location. In this case, it's memory, because you need to load the values to memory first, and then return them from the memory.
Your question doesn't specify if you need to return a new instance of the struct or load it from storage, so here's example of both:
pragma solidity ^0.8;
contract MyContract {
struct Briefing {
string description;
uint max_influencer;
uint jobvalue;
bool achievement;
}
Briefing summary;
function getSummaryHardcoded() public view returns (Briefing memory) {
return Briefing("desc", 1, 1, true);
}
function getSummaryStored() public view returns (Briefing memory) {
return summary;
}
}

Related

Use a variable to access key in struct

I have a struct like so :
struct test {
string a;
string b;
}
and a mapping like this:
mapping (address => test) public tests;
I want to have a function to update the struct like this:
function updateStruct (string _paramName, string _newValue) {
tests[msg.sender].(_paramName) = _newValue; // this is the line that shows the logic but doesn't work in compilation
}
How can I do this?
Solidity currently (v0.8) does not support accessing properties via magic variables.
You'll need to access the properties directly.
function updateStructA(string memory _newValue) public {
tests[msg.sender].a = _newValue;
}
function updateStructB(string memory _newValue) public {
tests[msg.sender].b = _newValue;
}

Solidity, Solc Error: Struct containing a (nested) mapping cannot be constructed

I am using Solc version 0.7.0 installed by npm. When I try to create a Struct that contains mapping, I received an error: "Struct containing a (nested) mapping cannot be constructed."
Please check the code:
// SPDX-License-Identifier: MIT
pragma solidity 0.7.0;
contract Test {
struct Request {
uint256 value;
mapping(address => bool) approvals;
}
Request[] public requests;
...
function createRequest(
uint256 value
) public {
Request memory newRequest = Request({// here the compiler complains
value: value
});
requests.push(newRequest);
}
}
When I use older versions of solc, the code compiles without problems.
Thank you in advance!
This should work:
function createRequest(uint256 value) public {
Request storage newRequest = requests.push();
newRequest.value = value;
}
Cheers!
This worked in my case:
struct Request{
uint256 value;
mapping(address => bool) approvals;
}
uint256 numRequests;
mapping (uint256 => Request) requests;
function createRequest (uint256 value) public{
Request storage r = requests[numRequests++];
r.value= value;
}

Mapping a structure inside a union in JNA

I am attempting to map the kstat library in Solaris 11.3 to Java using JNA. While I've managed to get most of the structures working, I've spent the last 24 hours fighting with a particularly difficult union-within-a-structure-within-a-union.
I am successfully retrieving a pointer to a kstat_named structure I need using kstat_data_lookup(). My code properly retrieves most of the data (name, data_type, and non-struct members of the union) in this C structure:
typedef struct kstat_named {
char name[KSTAT_STRLEN]; /* name of counter */
uchar_t data_type; /* data type */
union {
charc[16]; /* enough for 128-bit ints */
struct {
union {
char *ptr; /* NULL-terminated string */
} addr;
uint32_t len; /* length of string */
} str;
int32_t i32;
uint32_t ui32;
int64_t i64;
uint64_t ui64;
/* These structure members are obsolete */
int32_t l;
uint32_t ul;
int64_t ll;
uint64_t ull;
} value; /* value of counter */
} kstat_named_t;
I have mapped this in JNA as follows:
class KstatNamed extends Structure {
public static class UNION extends Union {
public byte[] charc = new byte[16]; // enough for 128-bit ints
public Pointer str; // KstatNamedString
public int i32;
public int ui32;
public long i64;
public long ui64;
}
public byte[] name = new byte[KSTAT_STRLEN]; // name of counter
public byte data_type; // data type
public UNION value; // value of counter
public KstatNamed() {
super();
}
public KstatNamed(Pointer p) {
super();
this.useMemory(p);
this.read();
}
#Override
public void read() {
super.read();
switch (data_type) {
case KSTAT_DATA_CHAR:
value.setType(byte[].class);
break;
case KSTAT_DATA_STRING:
value.setType(Pointer.class);
break;
case KSTAT_DATA_INT32:
case KSTAT_DATA_UINT32:
value.setType(int.class);
break;
case KSTAT_DATA_INT64:
case KSTAT_DATA_UINT64:
value.setType(long.class);
break;
default:
break;
}
value.read();
}
#Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "name", "data_type", "value" });
}
}
This code works correctly for int32 types (KSTAT_DATA_INT32). However, when the data type is KSTAT_DATA_STRING, which corresponds to the str structure inside the union, I am not having any success in properly retrieving the data.
I have mapped the nested structure like this:
class KstatNamedString extends Structure {
public static class UNION extends Union {
public Pointer ptr; // NULL-terminated string
}
public UNION addr;
public int len; // length of string
public KstatNamedString() {
super();
}
public KstatNamedString(Pointer p) {
super();
this.useMemory(p);
this.read();
}
#Override
public void read() {
super.read();
addr.setType(Pointer.class);
addr.read();
}
#Override
protected List<String> getFieldOrder() {
return Arrays.asList(new String[] { "addr", "len" });
}
}
Ultimately I'm trying to replicate the behavior of this C macro:
#define KSTAT_NAMED_STR_PTR(knptr) ((knptr)->value.str.addr.ptr)
I've tried multiple different methods of trying to get access to the above structure, but it never seems to read the correct data (the len value is in the millions and attempting to read the string ptr causes segfault). I've tried:
Pointer p = LibKstat.INSTANCE.kstat_data_lookup(ksp, name);
KstatNamed data = new KstatNamed(p);
KstatNamedString str = new KstatNamedString(data.value.str);
return str.addr.ptr.getString(0); // <--- Segfault on C side
I've also tried:
Specifying KstatNamedString as the type instead of the Pointer type
Using various combinations of ByReference in both the structures and the unions
I've googled everywhere, including trying what I thought was a promising result here, but nothing seems to work.
I'm sure I'm missing something simple.
Use KstatNamedString instead of Pointer type.
Change your pointer-based constructors like this:
public KstatNamed(Pointer p) {
super(p);
this.read();
}
public KstatNamedString(Pointer p) {
super(p);
this.read();
}
and change the addr field of the str struct field to be a simple Pointer (no need for the union bits around it).
public Pointer /*UNION*/ addr;
Run your JVM with -Djna.dump_memory=true and print your newly-initialized Structure as a string. That will show you how JNA interprets the memory layout of the struct, and how the native memory is initialized. That should help you determine how to extract the string you're looking for (assuming it's there).
You can also tune your union read() method to initially read only the type field (using Structure.readField("data_type")) before setting the union type.

How to initialize audio with Vala/SDL

I've been trying to figure this out for a few hours now.
In order to start up the audio, I need to create an SDL.AudioSpec object and pass it to SDL.Audio.Open. The problem is, AudioSpec is a class with a private constructor, so when I try to create one I get:
sdl.vala:18.25-18.43: error: `SDL.AudioSpec' does not have a default constructor
AudioSpec audiospec = new SDL.AudioSpec();
^^^^^^^^^^^^^^^^^^^
And if I try to just assign values to it's member vars like a struct (it's a struct in normal sdl) I get:
sdl.vala:20.3-20.25: error: use of possibly unassigned local variable `audiospec'
audiospec.freq = 22050;
^^^^^^^^^^^^^^^^^^^^^^^
I found the valac doc here: http://valadoc.org/sdl/SDL.AudioSpec.html
But it isn't much help at all.
The offending code block looks like this:
// setup the audio configuration
AudioSpec audiospec;
AudioSpec specback;
audiospec.freq = 22050;
audiospec.format = SDL.AudioFormat.S16LSB;
audiospec.channels = 2;
audiospec.samples = 512;
// try to initialize sound with these values
if (SDL.Audio.open(audiospec, specback) < 0)
{
stdout.printf("ERROR! Check audio settings!\n");
return 1;
}
Any help would be greatly appreciated!
Another update, as I'm still having some trouble. I've changed the vapi file, and this is what I have now:
public delegate void AudioCallback (out void* userdata, out uchar stream, int len);
[CCode (cname="SDL_AudioSpec")]
[Compact]
public struct AudioSpec {
public int freq;
public AudioFormat format;
public uchar channels;
public uchar silence;
public uint16 samples;
public uint16 padding;
public uint32 size;
public AudioCallback callback;
public void* userdata;
}// AudioSpec
I have a method that (tries?) to meet this function signature:
public void callback(out void* userdata, out uchar stream, int len)
{
stream = 0;
userdata = null;
}
And assigning it as:
audiospec.callback = gen.callback;
Needless to say, this still isn't working, get lots of errors:
/home/gukid/vala/soundgen.vala.c: In function ‘sound_gen_main’:
/home/gukid/vala/soundgen.vala.c:766:12: error: ‘SDL_AudioSpec’ has no member named ‘callback_target_destroy_notify’
/home/gukid/vala/soundgen.vala.c:766:72: error: ‘SDL_AudioSpec’ has no member named ‘callback_target_destroy_notify’
/home/gukid/vala/soundgen.vala.c:766:114: error: ‘SDL_AudioSpec’ has no member named ‘callback_target’
/home/gukid/vala/soundgen.vala.c:768:11: error: ‘SDL_AudioSpec’ has no member named ‘callback_target’
/home/gukid/vala/soundgen.vala.c:769:11: error: ‘SDL_AudioSpec’ has no member named ‘callback_target_destroy_notify’
/home/gukid/vala/soundgen.vala.c:770:21: warning: assignment from incompatible pointer type [enabled by default]
/home/gukid/vala/soundgen.vala.c:771:11: error: ‘SDL_AudioSpec’ has no member named ‘callback_target’
/home/gukid/vala/soundgen.vala.c:772:11: error: ‘SDL_AudioSpec’ has no member named ‘callback_target_destroy_notify’
error: cc exited with status 256
So I'm at a bit of another sticky point.
3rd post: EUREKA! I have a solution! (debatable :P)
First off, the sdl.vapi looks like:
[CCode (cheader_filename = "SDL.h")]
public delegate void AudioCallback (void* userdata, uchar* stream, int len);
[CCode (cname="SDL_AudioSpec", has_type_id=false)]
public struct AudioSpec {
public int freq;
public AudioFormat format;
public uchar channels;
public uchar silence;
public uint16 samples;
public uint16 padding;
public uint32 size;
[CCode (delegate_target = false, type = "void*")]
public weak AudioCallback callback;
public void* userdata;
}// AudioSpec
And then I can just create a function:
public static void callback(void* userdata, uchar* stream, int len)
And:
audiospec.callback = callback;
Ahhh, finally my beautiful white noise generator is complete!
I think it's a bug in the VAPI. In sdl.vapi, try changing
[Compact]
public class AudioSpec {
to
public struct AudioSpec {
and
[CCode (cname="SDL_OpenAudio")]
public static int open(AudioSpec desired, AudioSpec obtained);
to
[CCode (cname="SDL_OpenAudio")]
public static int open(AudioSpec desired, out AudioSpec obtained);
and make your code look like:
AudioSpec audiospec = AudioSpec();
AudioSpec specback;
audiospec.freq = 22050;
audiospec.format = SDL.AudioFormat.S16LSB;
audiospec.channels = 2;
audiospec.samples = 512;
// try to initialize sound with these values
if (SDL.Audio.open(audiospec, out specback) < 0)
{
stdout.printf("ERROR! Check audio settings!\n");
return 1;
}
and give it a test. It seems to generate the correct code according to the SDL docs. If it works, consider submitting the VAPI changes to the Vala bugzilla.
Okay, the delegate:
public delegate void AudioCallback (out void* userdata, out uchar stream, int len);
has the C type:
void(*AudioCallback)(void**userdata, unsigned char* stream, int len);
where userdata and stream are write-only, which is not really what you want.
void (*callback)(void *userdata, Uint8 *stream, int len);
In this, userdata is just a pointer to some stuff for your use. In Vala's terms, it's the target of a delegate. stream is an array, but SDL has allocated it, so it's not out and len is the length of the array, so we can have Vala deal with that automatically with just uint8[] stream. Putting it all together:
[CCode(instance_pos = 0)]
public delegate void AudioCallback(uint8[] stream);
The instance_pos tells Vala where the userdata is. It normally assumes it to be last. As for the struct:
[CCode (cname="SDL_AudioSpec", destroy_function = "")]
public struct AudioSpec {
...
[CCode(delegate_target_cname = "userdata")]
public AudioCallback callback;
}
This will create a callback that stores the target in userdata. Now, to avoid leaking memory, Vala is going to try to assign a destructor to callback_target_destroy_notify...and now we're stuck because we have no member for that. So, let's back up and try again:
[CCode(has_target = false)]
public delegate void AudioCallback(void* userdata, uint8[] stream);
The has_target tells Vala there is no userdata and we include it manually. As for the struct:
[CCode (cname="SDL_AudioSpec", destroy_function = "")]
public struct AudioSpec {
...
public AudioCallback callback;
public void* userdata;
}
So, userdata will get passed to your callback, but you'll have to cast it yourself and you'll have to manage the memory. Additionally, when the structs go out of scope, Vala is going to call a destroy function. By specifying an empty string, it will do nothing. Normally, this frees memory inside the struct, but we don't need to worry here.

efficiently calling unmanaged method taking unmanaged objects as parameters from managed code

I have the following scenario. The managed code will initialize lots of object of a class which is a wrapper around an unmanaged struct. There are two approaches that I can do for this. One is to have a managed class wrapper that just has a pointer to the unmanaged object. The other is to have a full fledged managed class and create the unmanaged object when required to call into unmanaged methods. I have provided both the methods below. I was told that if I use the approach 1(having a pointer to unmanged object), the GC will have lots of issue knowing about the unmanaged portion and it is better to do approach 2. Does someone tell me which is better or if there is some other approach that is even better. My concern with Approach 2 is that there are copying to and fro everytime a unmanaged method is called. I am not sure if the GC issue outweighs it.
EDIT- the first approach has a ref class and the second has a value class. The reason the second is value is so that it can be added to lists more efficiently
In unmanaged:
struct A_UNMANAGED
{
int a;
int b[20];
};
void GetData(A_UNMANAGED& a); // populates A
In managed (First Approach)
public ref class A_MANAGED
{
A_UNMANGED* ap;
public:
property System::UInt32 a
{
System::UInt32 get() { return ap->a; }
void set(System::UInt32 value) { ap->a = value; }
}
property array<System::UInt32>^ b
{
array<System::UInt32>^ get() { return ap->b; }
void set(array<System::UInt32>^ value) { b = value; } // assume this copy works
}
internal:
void GetData()
{
GetData(ap);
}
};
In managed (Second Approach) (EDIT: updated to ref. Assume all the garbage collection and pointer creation is written correctly)
public value class A_MANAGED
{
System::UInt32 a;
array<System::UInt32>^ b;
public:
property System::UInt32 a
{
System::UInt32 get() { return a; }
void set(System::UInt32 value) { a = value; }
}
property array<System::UInt32>^ b
{
array<System::UInt32>^ get() { return b; }
void set(array<System::UInt32>^ value) { b = value; }
}
internal:
void GetUnmanaged(A_UNMANAGED& obj1)
{
obj1.a = a;
pin_ptr<System::UInt32> bp = &b[0];
memcpy(obj1.b, bp, 20);
}
void GetData()
{
A_UNMANAGED obj2;
GetUnmanaged(obj2);
GetData(obj2);
// copy from obj2 to member variables
}
};
No, the 1st snippet it the canonical way. The garbage collector only moves the pointer, it doesn't move the pointed-to object. That one should have been allocated with malloc() or the new operator, it cannot be moved.
There are otherwise several serious problems in your code. You don't seem to allocate the memory for A_UNMANAGED unless GetData() takes its argument by reference. GetData() is never called. This must normally be a ref class (not ref value) so you can provide a destructor and a finalizer to release the memory. The b property setter will bomb your program with a StackOverflowException. Be sure to study the language before tackling this project.
Check this answer for sample code.
As Hans said, the first way is the usual approach (though personally, I think P/Invoke would be more succinct in this particular case...). However, your A_MANAGED::b implementation will not work, which would be obvious if one were to try simply compiling it. Try this instead:
public ref class A_MANAGED
{
A_UNMANAGED* ap;
public:
A_MANAGED() : ap(new A_UNMANAGED() ) { }
~A_MANAGED() { this->!A_MANAGED(); }
!A_MANAGED() { delete ap; ap = nullptr; }
property int a
{
int get() { return ap->a; }
void set(int value) { ap->a = value; }
}
property array<int>^ b
{
array<int>^ get()
{
using System::Runtime::InteropServices::Marshal;
array<int>^ arr = gcnew array<int>(20);
Marshal::Copy(System::IntPtr(ap->b), arr, 0, 20);
return arr;
}
void set(array<int>^ value)
{
using System::Runtime::InteropServices::Marshal;
Marshal::Copy(value, 0, System::IntPtr(ap->b), 20);
}
}
internal:
void GetData()
{
::GetData(*ap);
}
};
And then there's the usual caveat about returning arrays from properties: it's a bad idea. Unless you really want to maintain parity with the unmanaged class' public interface, b should really be a pair of set/get functions rather than a property.

Resources