Hi I have an struct form a c project I need to include in java.
struct usb_relay_device_info {
unsigned char *serial_number;
char *device_path;
usb_relay_device_type type;
usb_relay_device_info* next;};
I created a Java class
public class RelayInfo extends Structure {
public String device_path;
public RelayInfo next;
public byte[] serial_number;
public RelayType type;
#Override
protected List getFieldOrder() {
return Arrays.asList("device_path", "next", "serial_number", "type");
}}
when I try to run my project I get an stackOverwlowError
Exception in thread "main" java.lang.StackOverflowError
at java.util.Hashtable.get(Hashtable.java:363)
at java.util.Properties.getProperty(Properties.java:969)
at java.util.Properties.getProperty(Properties.java:988)
at java.lang.System.getProperty(System.java:756)
at com.sun.jna.Native.getDefaultStringEncoding(Native.java:669)
at com.sun.jna.Native.getStringEncoding(Native.java:662)
at com.sun.jna.Structure.<init>(Structure.java:177)
at com.sun.jna.Structure.<init>(Structure.java:172)
at com.sun.jna.Structure.<init>(Structure.java:159)
at com.sun.jna.Structure.<init>(Structure.java:151)
at relay.RelayInfo.<init>(RelayInfo.java:11)
at sun.reflect.GeneratedConstructorAccessor1.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
at java.lang.Class.newInstance(Class.java:442)
at com.sun.jna.Structure.newInstance(Structure.java:1773)
at com.sun.jna.Structure.newInstance(Structure.java:1759)
at com.sun.jna.Structure.size(Structure.java:1030)
at com.sun.jna.Native.getNativeSize(Native.java:1172)
at com.sun.jna.Structure.getNativeSize(Structure.java:2072)
at com.sun.jna.Structure.getNativeSize(Structure.java:2062)
at com.sun.jna.Structure.validateField(Structure.java:1105)
at com.sun.jna.Structure.validateFields(Structure.java:1119)
at com.sun.jna.Structure.<init>(Structure.java:179)
at com.sun.jna.Structure.<init>(Structure.java:172)
at com.sun.jna.Structure.<init>(Structure.java:159)
at com.sun.jna.Structure.<init>(Structure.java:151)
at relay.RelayInfo.<init>(RelayInfo.java:11)
at sun.reflect.GeneratedConstructorAccessor1.newInstance(Unknown Source)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
When I debug it says Method threw'java.lang.StrackOverflowError' exception. Cannot evaluate relay.RelayInfo.toString()
But with JNA i don't need a toString overwrite or?
what is wrong? Anybody an idea?
Thanks vor helbing
Found my Answer.
Don't know how but it is working
public class RelayInfo extends Structure {
/*
struct RelayInfo {
unsigned char *serial_number;
char *device_path;
usb_relay_device_type type;
usb_relay_device_info* next;
};
*/
public ByteByReference serial_number;
public String device_path;
public int type;
public String next;
#Override
protected List getFieldOrder() {
return Arrays.asList("serial_number", "device_path", "type", "next");
}}
Thats how it works for the relay.
Thanks to everybody how tried to help me :)
There are two issues involved here: type mapping, and the order of your arguments.
Your Java structure must exactly match the order of the variables in the C structure, as JNA will map the variables by offset. I understand from the comments that you've already fixed that.
Secondly, you need to be careful with the type mappings. While a char * can be a String, it also may be a ByteByReference so you need to look at the context of the C structure to decide which it is. I believe you've correctly mapped String for the device_path but I strongly suspect the serial_number is a ByteByReference and not the byte[] you've defined (which you would need to initialize, anyway; since you don't have a length that's a clue it's not right). Finally, you've defined a variable of type RelayType but haven't written the JNA structure that maps to the corresponding C usb_relay_device_type. (EDIT: I see you've clarified that's an enum: that maps to an int.)
It's node structure like that
So try to use RelayInfo wraped in PointerByReference. And keep field next as RelayInfo.ByReference
Related
Background: I found "dysfunctional" code in spring-admin project: "Cannot construct instance of Registration (no Creators, like default construct, exist)". So I wrote custom deserializer and report the issue. But report was rejected, since it allegedly works. And after retest it seems to work now. Does not make sense. So I would like to know why that code work.
But here is the catch. When I wrote similar test class, it does not work in my project. Even when I literally take the code of "now-working" Registration class, and try it in own project, is simply does not deserialize. And then, with practically identical class, it works. It doesn't make any sense.
https://github.com/codecentric/spring-boot-admin/blob/master/spring-boot-admin-server/src/main/java/de/codecentric/boot/admin/server/domain/values/Registration.java
Following post explains how lombok-jackson combo works, but it does not work here. I'm totally confused, this is unbelievelably ridiculous situation, where (unnecessary) simplification creates superb complexity. But I'd like to understand it, since I can encounted this situation in future again.
Jackson Deserialization Fails because of non-default constructor created by lombok
So to have something easy to work with: here we have nice&working pure jackson:
public class TestTO_pureJackson {
private final String a;
private final String b;
#JsonCreator
private TestTO_pureJackson(#JsonProperty("a") String a, #JsonProperty("b") String b) {
this.a = a;
this.b = b;
}
}
and here we have not working lombok equivalent (even if I remove one field, so that it's "same" to latter example):
#lombok.Data
public class TestTO {
private final String a;
private final String b;
#lombok.Builder(builderClassName = "Builder")
private TestTO(String a, String b) {
this.a = a;
this.b = b;
}
public static TestTO.Builder create(String a) {
return builder().a(a);
}
}
and we are trying to deserialize:
{"a": "a", "b": "b"}
Can anyone understand the magic under the hood, and help me to understand what's wrong here?
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
<scope>provided</scope>
</dependency>
And to make it even more ridiculous (do you actually see any significant difference with TestTO???), following code works:
#lombok.Data
public class Pair {
private final String left;
private final String right;
#lombok.Builder(builderClassName = "Builder")
private Pair(String pairId) {
left = pairId.substring(0, 3).toUpperCase(Locale.US);
right = pairId.substring(3).toUpperCase(Locale.US);
}
}
and main method:
public class PairTest {
public static final String DATA = "[\"btcusd\",\"ltcusd\",\"ltcbtc\"]";
public static void main(String[] args) throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
Pair[] pairs = objectMapper.readValue(DATA, Pair[].class);
for (Pair pair : pairs) {
System.out.println(pair);
}
}
}
Can anyone see, why 2 almost same TO classes behave differently?
TestTO does not work because there is no constructor that Jackson can use. It cannot use the two-args constructor, because it does not know which JSON field should be used for which argument (because argument names are removed during compilation).
For lombok-generated constructors, you can work around that by advising Lombok to generate a #ConstructorProperties annotation. Just add
lombok.anyConstructor.addConstructorProperties=true
to your lombok.config. In your case of a manual constructor, you could also simply add the #JsonPropertys.
(Note that Jackson does not automatically use builders; you have to explicitly tell Jackson that with #JsonDeserialize and #JsonPOJOBuilder.)
TestTO_pureJackson works, because #JsonProperty is available at runtime and used by Jackson to determine the mapping.
Pair works, because there is a usable constructor: Jackson does not have to guess which parameter belongs to which field, because there is just one. Note that this only works for String, int, long or boolean one-arg constructors.
Lombok does not generate any additional constructor (here: the two-args constructor) if there is already one (see documentation of #Data), so this is the only constructor on the class.
I would like to create a calculator application that can switch between different number bases. As far as entering digits is concerned, I was thinking the following would be a flexible api:
public interface ICalculator
{
string Enter(INumberElement element);
}
public class BaseTenCalculator : ICalculator
{
public string Enter(INumberElement element)
{
...
}
}
public class BaseTwoCalculator : ICalculator
{
public string Enter(INumberElement element)
{
...
}
}
My problem is that for the BaseTenCalculator, I would like a method
Enter(BaseTenNumberElement element)
and for a BaseTwoCalculator, I would like a method
Enter(BaseTwoNumberElement element)
to make sure only valid digits for that number base get entered. However, the only way I can think of enforcing this constraint is downcasting the 'element' argument in the two different implementations, and throwing an exception if INumberElement is not of the correct type. I feel like this is 'wrong', and I'm missing something. Is there another way? Is it even possible to create a common interface for two different number base calculators?
public interface ICalculator<in T> where T : INumberElement
{
string Enter(T element);
}
public class BaseTenCalculator : ICalculator<BaseTenNumberElement>
{
public string Enter(BaseTenNumberElement element)
{
throw new NotImplementedException();
}
}
public class BaseTwoCalculator : ICalculator<BaseTwoNumberElement>
{
public string Enter(BaseTwoNumberElement element)
{
throw new NotImplementedException();
}
}
I think you're thinking of the problem incorrectly. A number is a number regardless of base. Base is only a visible representation of the number. A good example to work from might be BigInteger. It has a constructor: BigInteger(String val, int radix), and a function: toString(int radix). All the work of representing the number is done the same. The only thing that differs is parsing from a string representation into the number, and then getting back out into a number format in a particular base.
You could create a multi-base calculator by using BigInteger or BigDecimal underneath and just using a base selection to set the radix value to parse or print the number(s). You'd also want to limit the input buttons (assuming you're using buttons), but that's really just a counting problem.
I have my Keyboard class:
namespace BSGameFramework
{
namespace Input
{
static public ref class Keyboard
{
public:
static KeyboardState GetState();
};
}
}
Where KeyboardState is a ref struct.
After compilation as dll from my C# application I see the function from metadata as follow:
namespace BSGameFramework.Input
{
public class Keyboard
{
public Keyboard();
public static void GetState(ref KeyboardState value);
}
}
Keyboard class has lost its static state and the function GetState is now returning void and taking a KeyboardState as referenced parameter!
Somebody know why? Thanks in advance :D
The reason is because of the return type, ref struct KeyboardState.
In C++/CLI, the "ref" vs. "value" is the thing that determines whether a type is a reference type or a value type, not "class" vs. "struct". ref class and ref struct are both the same thing as C#'s class. value class and value struct are both the same thing as C#'s struct.
Therefore, you have a C++/CLI method declared as returning a reference type, but without the ^. This data type does exist in C++/CLI, but not in C#. The method signature you see is a workaround.
There are two possible solutions to this issue:
Change KeyboardState to a value struct. From what you said, it sounds like you intended for this to be a value type from the beginning, so this is probably the best solution.
Change the return type of the method to KeyboardState^. This will let the method show up in C# the same as it does in C++/CLI. However, if you do this, you'll want to switch all uses of KeyboardState to KeyboardState^. It's a reference type, it should be used with a ^.
I have the problem described here:
http://social.msdn.microsoft.com/Forums/en-AU/csharplanguage/thread/b310c71a-2479-4a93-888a-29294cecbe09
They give a solution using a SerializationBinder. Is there another alternative?? Like decorating my classes with a different namespace and assembly?? The reason is that I have some classes with this problem used many times, and I have to add the line "formatter.Binder = ..." in each part of the code. It would be easier to apply my hipothetic second solution.
Thanks.
If the assembly version changes, serialized objects become invalid. I once made changes to the source code of Protobuf-Net to avoid the version check, and it was fairly easy to do so. However, it can lead to unexpected results (data ending up in the wrong fields), unless you avoid the implicit fields, and set an index to each field manually using annotations. That's the advantage or Protobuf-Net, that you have control over the order of the fields in the serialized stream.
Another solution is to use custom serialization? Something like:
[Serializable]
public class MyObject : ISerializable
{
public int n1;
public int n2;
public String str;
public MyObject()
{
}
protected MyObject(SerializationInfo info, StreamingContext context)
{
n1 = info.GetInt32("i");
n2 = info.GetInt32("j");
str = info.GetString("k");
}
[SecurityPermissionAttribute(SecurityAction.Demand,
SerializationFormatter =true)]
public virtual void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("i", n1);
info.AddValue("j", n2);
info.AddValue("k", str);
}
}
I am attempting to simply add a FilterInfo class to my FilterInfo collection. I'm having a terrible time trying to understand why the following code keeps throwing the error:
System::Collections::Generic::List::Add'
: cannot convert parameter 1 from
'Ziz::FilterInfo *' to
'Ziz::FilterInfo'
I'm only learning C++/CLI, as I'm a C# developer, and I'm sure it's something simple, but I sure could use some pointers. My stripped code is as follows:
public value class FilterInfo
{
public:
char* Address;
};
public ref class Ziz
{
private:
List<FilterInfo>^ _blockList;
public:
// Constructor
Ziz(){
_blockList = gcnew List<FilterInfo>();
}
List<FilterInfo>^ GetBlockList()
{
for each(_IPFILTERINFO ip in _packetFilter->GetBlockList())
{
// _IPFILTERINFO is the native C++ struct.
FilterInfo* f = new FilterInfo();
_blockList->Add(f);
}
return _blockList;
}
You declared _blockList as
List<FilterInfo>^ _blockList;
but you are trying to add
FilterInfo* f
to it. It cannot work since one is a pointer and the other one is a reference.
I'm not sure how "value" fits in but in
public value class FilterInfo
{
public:
char* Address;
};
You are derefore declaring an unmanaged class
to make it managed, you should use
public ref class FiterInfo
This will allow you to use FilterInfo* without having to manage memory explicitely.
Finally, char* is not so great in C++/CLI, I would recommend using System::String
_blockList->Add(*f);
You have to construct your FilterInfo with gcnew as well. You can't really mix and mash these together without marshaling.
FilterInfo is not FilterInfo*. If you want a List of pointers to FilterInfo, you need to say that List<FilterInfo*>. Since FilterInfo is a value class here though you'll likely just want to skip the new.
FilterInfo fi;
_blockList->Add(fi);
public ref class A
{
};
int main(array<System::String ^> ^args)
{
Console::WriteLine(L"Hello World");
ICollection<A^>^ oCollection = gcnew List<A^>();
oCollection->Add(gcnew A());
return 0;
}