after switching unity scripting to IL2CPP scripting my android build is gettting a lot of undefined reference errors. A lot of them reference to IOS ARKit related stuff. Can i use #if !UNITY_IOS statements to remove them from my android build ? and where can i put these #if statements ?
errors are realated to Bulk_Assembly-CSharp_8.cpp.. i tried putting #if statements in it for platform specific build but does not seem to make a difference
if !UNITY_IOS
public class ARFaceAnchor
{
private UnityARFaceAnchorData faceAnchorData;
private static Dictionary<string, float> blendshapesDictionary;
public ARFaceAnchor (UnityARFaceAnchorData ufad)
{
faceAnchorData = ufad;
if (blendshapesDictionary == null) {
blendshapesDictionary = new Dictionary<string, float> ();
}
}
public string identifierStr { get { return faceAnchorData.identifierStr; } }
public Matrix4x4 transform {
get {
Matrix4x4 matrix = new Matrix4x4 ();
matrix.SetColumn (0, faceAnchorData.transform.column0);
matrix.SetColumn (1, faceAnchorData.transform.column1);
matrix.SetColumn (2, faceAnchorData.transform.column2);
matrix.SetColumn (3, faceAnchorData.transform.column3);
return matrix;
}
}
public ARFaceGeometry faceGeometry { get { return new ARFaceGeometry (faceAnchorData.faceGeometry); } }
public Dictionary<string, float> blendShapes { get { return GetBlendShapesFromNative(faceAnchorData.blendShapes); } }
delegate void DictionaryVisitorHandler(IntPtr keyPtr, float value);
[DllImport("__Internal")]
private static extern void GetBlendShapesInfo(IntPtr ptrDic, DictionaryVisitorHandler handler);
Dictionary<string, float> GetBlendShapesFromNative(IntPtr blendShapesPtr)
{
blendshapesDictionary.Clear ();
GetBlendShapesInfo (blendShapesPtr, AddElementToManagedDictionary);
return blendshapesDictionary;
}
[MonoPInvokeCallback(typeof(DictionaryVisitorHandler))]
static void AddElementToManagedDictionary(IntPtr keyPtr, float value)
{
string key = Marshal.PtrToStringAuto(keyPtr);
blendshapesDictionary.Add(key, value);
}
}
endif
If you want to remove from Android build, use
#if !UNITY_ANDROID
// your code here
#endif
Will be more helpful if you can post your error message is pertaining to which line.
Thanks Darren,
i must have got it all wrong. so
if !UNITY_ANDROID means if NOT Android ?
Related
I am new to javacpp i know java have not much experience in c++. This might be one of very simple question but i am struggling with this. How to access any variable type value written in header of c++ into java code using javacpp. Let us consider example:
C++ Code example:
There is function written in C++ which return the frame of video below is the code for it and expects an Struct argument to be passed.
unsigned char *
Videodecode::getframe_data (void *ptr)
{
GstSample *sample;
GstBuffer *buffer;
GstMapInfo map;
GstCaps *caps;
GstStructure *str;
gint width, height;
gstData *dataa = (gstData *) ptr;
sample = gst_app_sink_pull_sample ((GstAppSink*)dataa->sink);
if (sample != NULL) {
buffer = gst_sample_get_buffer (sample);
gst_buffer_map (buffer, &map, GST_MAP_READ);
if (map.data != NULL) {
caps = gst_sample_get_caps (sample);
if (caps != NULL);
str = gst_caps_get_structure (caps, 0);
if (!gst_structure_get_int (str, "width", &width) ||
!gst_structure_get_int (str, "height", &height)) {
g_print ("No width/height available\n");
}
display_data = map.data;
//displayImg = Mat (Size (width, height ), CV_8UC3, map.data);
// cvtColor (displayImg, displayImg, COLOR_YUV2BGR_YUY2);
gst_buffer_unmap (buffer, &map);
gst_buffer_unref (buffer);
} else
gst_sample_unref (sample);
}
else {
//cout << "gstImageBuffer is NULL" << endl;
return NULL;
}
//return displayImg.data;
return display_data;
}
The structure which need to be passed as argument is below
typedef struct gstData_t
{
GstElement *pipeline;
GstElement *source;
GstElement *demux;
GstElement *parser;
GstElement *decoder;
GstElement *convert;
GstElement *capsfilter;
GstElement *sink;
GstElement *typefind;
} gstData;
Corresponding java code written to access it is below:
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import org.bytedeco.javacpp.FunctionPointer;
import org.bytedeco.javacpp.Loader;
import org.bytedeco.javacpp.Pointer;
import org.bytedeco.javacpp.annotation.Name;
import org.bytedeco.javacpp.annotation.NoOffset;
import org.bytedeco.javacpp.annotation.Platform;
import org.bytedeco.javacpp.annotation.Raw;
import org.bytedeco.javacpp.tools.Builder;
import org.bytedeco.javacpp.tools.ParserException;
#Platform(include = {"Videodecode.h",
},
includepath = {"/usr/include/gstreamer-1.0/","/usr/include/glib-2.0/","/usr/lib/x86_64-linux-gnu/glib-2.0/include/"},
//linkpath = {"/home/ign/git/JavaCppExample/src/main/resources/de/oltzen/javacppexample/"},
link = {"Videodecode"})
public class Videodecode {
NativeVideodecode nda;
static {
Class c = Videodecode.class;
Builder builder = null;
try {
builder = new Builder().classesOrPackages(c.getName());
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (NoClassDefFoundError e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
try {
File[] outputFiles = builder.build();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ParserException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Loader.load(c);
// Loader.load();
}
public Videodecode() {
nda = new NativeVideodecode();
}
public Videodecode(String filename) {
nda = new NativeVideodecode(filename);
}
public boolean filePathCpp(String str){
return nda.filePathCpp(str);
}
public boolean settingValCpp(String str){
return nda.settingValCpp(str);
}
public boolean process_event (int event) {
return nda.process_event(event);
}
public java.nio.ByteBuffer test1122 (String buffer) {
return nda.test1122(buffer);
}
public java.nio.ByteBuffer test112233 (String buffer) {
return nda.test1122(buffer);
}
public java.nio.ByteBuffer getframe_data(java.nio.ByteBuffer buffer){
return nda.getframe_data(buffer);
}
public Pointer gstData(){
return nda.gstData();
}
#Name("Videodecode")
public static class NativeVideodecode extends Pointer {
static {
Loader.load();
}
public NativeVideodecode() {
allocate();
}
public NativeVideodecode(String filename) {
System.out.println("filename "+filename);
allocate(filename);
}
public NativeVideodecode(Pointer p) {
super(p);
}
private native void allocate(String filename);
private native void allocate();
private native boolean filePathCpp(String str);
private native boolean settingValCpp(String str);
private native boolean process_event(int event);
private native java.nio.ByteBuffer test1122(String buffer);
private native java.nio.ByteBuffer test112233(String buffer);
// private native boolean test1122(byte[] buffer);
private native java.nio.ByteBuffer getframe_data (java.nio.ByteBuffer buffer);
#NoOffset private native Pointer gstData();
}
}
Problems being faced by me :
How to access Struct from C++ and pass it as an argument using java.
How to access frame data which is unsigned char*.
Approach which i tried to perform this.
To access Struct, i tried using offsetof but not sure how to use it in javacpp.
To access frame data i tried using java.nio.ByteBuffer but seems its not working properly.
While trying to compile code using mvn clean install below error is getting triggered.
[INFO] --- javacpp:1.3:build (javacpp.compiler) # projecustom ---
[INFO] Detected platform "linux-x86_64"
[INFO] Building for platform "linux-x86_64"
[WARNING] Could not load platform properties for class com.proje.decoder.connectorJavaCpp
[WARNING] Could not load platform properties for class com.proje.decoder.test1234
[INFO] Generating /home/ign/eclipse-workspace/projecustom/target/classes/com/proje/decoder/jniVideodecode.cpp
[INFO] Compiling /home/ign/eclipse-workspace/projecustom/target/classes/com/proje/decoder/linux-x86_64/libjniVideodecode.so
[INFO] g++ -I/usr/include/gstreamer-1.0/ -I/usr/include/glib-2.0/ -I/usr/lib/x86_64-linux-gnu/glib-2.0/include/ -I/usr/lib/jvm/java-8-openjdk-amd64/include -I/usr/lib/jvm/java-8-openjdk-amd64/include/linux /home/ign/eclipse-workspace/projecustom/target/classes/com/proje/decoder/jniVideodecode.cpp -march=x86-64 -m64 -O3 -s -Wl,-rpath,$ORIGIN/ -Wl,-z,noexecstack -Wl,-Bsymbolic -Wall -fPIC -shared -o libjniVideodecode.so -lVideodecode
/home/ign/eclipse-workspace/projecustom/target/classes/com/proje/decoder/jniVideodecode.cpp: In function ‘_jobject* Java_com_proje_decoder_Videodecode_00024NativeVideodecode_gstData(JNIEnv*, jobject)’:
/home/ign/eclipse-workspace/projecustom/target/classes/com/proje/decoder/jniVideodecode.cpp:1532:21: error: ‘class Videodecode’ has no member named ‘gstData’
rptr = ptr->gstData();
edit:
let me try to take one simple example :
C++ Code:
#include <stdio.h>
struct test
{
int a;
std::string b;
};
class Foo {
public:
int n;
int m=70;
test tst;
// tst.a=10;
// tst.b="hi";
Foo(int n) : n(n) { }
virtual ~Foo() { }
virtual void bar() {
printf("Callback in C++ (n == %d)\n", n);
}
};
void callback(Foo *foo) {
foo->bar();
}
is it possible to write modify java code below to access a and b variables of struct
package com.ign.examples;
import org.bytedeco.javacpp.*;
import org.bytedeco.javacpp.annotation.*;
#Platform(include="Foo.h")
public class VirtualFoo1 {
static { Loader.load(); }
public static class Foo extends Pointer {
static { Loader.load(); }
public Foo(int n) { allocate(n); }
private native void allocate(int n);
#NoOffset public native int n(); public native Foo n(int n);
#Virtual public native void bar();
public native int m(); public native void m(int m);
// public native #Cast("int") int a(); public native Foo a(int a);
public native Pointer tst(); public native void tst(Pointer tst);
}
public static native void callback(Foo foo);
public static void main(String[] args) {
Foo foo = new Foo(13);
System.out.println(foo.m());
}
}
When installing "Crashlytics" in my Android App, it automatically installs "Answers". I only want to install "Crashlytics" and want to have "Answers" disabled. Does anyone know how to do that?
build.gradle
dependencies {
compile('com.crashlytics.sdk.android:crashlytics:2.5.5#aar') {
transitive = true;
}
Thanks!
Mike from Fabric and Crashlytics here.
As you saw, Crashlytics by default includes Answers. If you don't want Answers enabled on your app, then you want to invoke CrashlyticsCore() when building your app instead of Crashlytics.
For example, have these as your imports:
import com.crashlytics.android.Crashlytics;
import com.crashlytics.android.core.CrashlyticsCore;
import io.fabric.sdk.android.Fabric;
Then when you initialize Fabric, use:
Fabric.with(this, new CrashlyticsCore());
and that will initialize only the crash reporting side of things.
I think it is not possible at the moment, because Crashlytics is making an Answers instance:
public Crashlytics build() {
if(this.coreBuilder != null) {
if(this.core != null) {
throw new IllegalStateException("Must not use Deprecated methods delay(), disabled(), listener(), pinningInfoProvider() with core()");
}
this.core = this.coreBuilder.build();
}
if(this.answers == null) {
this.answers = new Answers();
}
if(this.beta == null) {
this.beta = new Beta();
}
if(this.core == null) {
this.core = new CrashlyticsCore();
}
return new Crashlytics(this.answers, this.beta, this.core);
}
Crashlytics(Answers answers, Beta beta, CrashlyticsCore core) is not public, so we cannot instanciate this. Also the answers field is final, so you cannot override it. I think there is now way to let the user decide if they want to use answers.
Hey Fabric/Google, you should make a programmatically way to opt out for a session, to give the programmer the option to let the user decide if they want to be counted in some way.
EDIT
My solution is to to use a wraper for all needed funtioncs and init, feel free to use it:
public class AnalyticsWrapper {
static AnalyticsWrapper instance;
public static void initialize(Context context, boolean optOut) {
if (instance != null) throw new IllegalStateException("AnalyticsWrapper must only be initialized once");
instance = new AnalyticsWrapper(context.getApplicationContext(), optOut);
}
public static AnalyticsWrapper getInstance() {
if (instance == null) throw new IllegalStateException("AnalyticsWrapper must be initialized before");
return instance;
}
final boolean optOut;
private AnalyticsWrapper(Context context, boolean optOut) {
this.optOut = optOut;
initFabric(context);
}
public boolean isOptOut() {
return optOut;
}
private void initFabric(Context context) {
if (!optOut) {
if (!BuildConfig.DEBUG) {
Timber.plant(new CrashlyticsLogExceptionTree());
Timber.plant(new CrashlyticsLogTree(Log.INFO));
}
Crashlytics crashlyticsKit = new Crashlytics.Builder().core(
new CrashlyticsCore.Builder().disabled(BuildConfig.DEBUG)
.build())
.build();
Fabric fabric = new Fabric.Builder(context).kits(crashlyticsKit)
.debuggable(BuildConfig.DEBUG)
.build();
Fabric.with(fabric);
}
}
public void crashlyticsSetString(String key, String value) {
if (!optOut) {
Crashlytics.setString(key, value);
}
}
public void logException(Throwable throwable) {
if (!optOut) {
Crashlytics.logException(throwable);
}
}
public void logEvent(CustomEvent event) {
if (!optOut) {
Answers.getInstance()
.logCustom(event);
}
}
}
I have a large solution with 50+ unmanaged projects in it. I have recently added a project with managed code in it to the solution. The managed code accesses Windows.Devices.Sensors in a .NET dll. This dll is eventually wrapped by unmanaged code and called from another unmanaged project.
My problem is that I get the following access violation before main() even executes.
Unhandled exception at 0x744b8ea0 in myApplication.exe: 0xC0000005: Access violation.
Managed code:
#using <Windows.winmd>
using namespace Windows::Devices::Sensors;
#include <math.h>
namespace TabletSensors
{
namespace NET
{
public ref class DotNetDllClass
{
public:
DotNetDllClass()
{
Initialization();
}
~DotNetDllClass()
{
}
float* GetQuaternion()
{
OrientationSensorReading^ reading = _orientation->GetCurrentReading();
if( reading != nullptr )
{
float* quat = new float[4];
quat[0] = reading->Quaternion->X;
quat[1] = reading->Quaternion->Y;
quat[2] = reading->Quaternion->Z;
quat[3] = reading->Quaternion->W;
return quat;
}
else
{
return NULL;
}
}
private:
void Initialization()
{
_orientation = OrientationSensor::GetDefault();
if( _orientation != nullptr )
{
_orientation->ReportInterval = 16;
}
else
{
// not good ... throw exception or something
}
}
OrientationSensor^ _orientation;
};
}
}
Wrapper header file:
namespace TabletSensors
{
namespace NETWrapper
{
class DLLEXPORT_SENSORS WrapperClass
{
public:
__stdcall WrapperClass();
__stdcall ~WrapperClass();
float* __stdcall GetQuaternion();
};
}
}
Wrapper cpp file:
#define MIXSENSORS_BUILD
#include <gcroot.h>
#include "DotNetWrapper.h"
#include "DotNetDll.h"
using namespace TabletSensors::NETWrapper;
using namespace TabletSensors::NET;
static gcroot<TabletSensors::NET::DotNetDllClass^> Sensors = nullptr;
static System::UInt16 refCount = 0;
#pragma managed
inline TabletSensors::NET::DotNetDllClass^ GetSensors(void)
{
return (TabletSensors::NET::DotNetDllClass^)Sensors;
}
void Init()
{
++refCount;
if(GetSensors() == nullptr)
{
Sensors = gcnew TabletSensors::NET::DotNetDllClass();
}
}
void CleanUp()
{
if( refCount > 0 )
{
--refCount;
}
}
float* GetQuaternion_()
{
return Sensors->GetQuaternion();
}
#pragma unmanaged
TabletSensors::NETWrapper::WrapperClass::WrapperClass()
{
Init();
}
TabletSensors::NETWrapper::WrapperClass::~WrapperClass()
{
CleanUp();
}
float* TabletSensors::NETWrapper::WrapperClass::GetQuaternion()
{
float* x = new float[4];
return GetQuaternion_();
}
#pragma managed
Unmanaged project referencing my wrapper class:
#include "DotNetWrapper.h"
.
.
.
void UnmanagedProject::Update()
{
// if this line is present, I get an access violation without hitting any breakpoints.
TabletSensors::NETWrapper::WrapperClass _tabletSensors;
.
.
.
}
Since the managed code is trying to access Tablet Sensors I understand why it doesn't work on my Windows 7 desktop. What I don't understand it why it won't even allow me to debug my code at all. No breakpoints are hit before the Access Violation occurs.
What I would really like to figure out is how to use exception handling or #ifdefs to keep this crash from happening. But I have had very little luck.
Any ideas?
The fix is to Delay Load the managed DLL. The allows the application to run until that DLL is explicitly called. Thanks to Ben Voight for his answer here: https://stackoverflow.com/a/28467701/1454861
i was wondering if anyone could give me pointers on how to go about creating pdf files dynamically(without having to save the file as a pdf) from the vala language. i heard it can be done with libharu so ive been looking into their documentation but its still kinda hazy for me. does anyone know how to go about...
sending written annotations/text from a UI created with vala, to libharu? and having libharu create a pdf from it?
help would be much appreciated. thanks!
Even if the question is quite old I was in need of something similar...
As nemequ said you need to write a vapi to wrap the library.
This is a minimal one based on libhpdf 2.0.8 (not the latest)
=== Filename: haru.vapi ===
[CCode(cheader_filename="hpdf.h", cprefix="HPDF_")]
namespace HPDF {
[CCode(cname="HPDF_STATUS")]
public struct Status : ulong {
}
[CCode(cname="HPDF_REAL")]
public struct Real : float {
}
[CCode(cname="HPDF_Error_Handler", instance_pos = -1)]
public delegate void ErrorHandler (Status error_no, Status detail_no);
[Compact]
[CCode(free_function="HPDF_Free", cname="HPDF_Doc")]
public class Doc {
[CCode(cname="HPDF_New", instance_pos = -1)]
public Doc (ErrorHandler error_handler);
[CCode(cname="HPDF_AddPage")]
public unowned Page add_page();
[CCode(cname="HPDF_GetFont")]
public unowned Font get_font(string name, string? encoding = null);
[CCode(cname="HPDF_SaveToFile")]
public Status save_to_file (string file);
}
[Compact]
[CCode(cname="HPDF_Page")]
public class Page {
[CCode(cname="HPDF_Page_SetFontAndSize")]
public Status set_font_and_size (Font font, float size);
[CCode(cname="HPDF_Page_BeginText")]
public Status begin_text ();
[CCode(cname="HPDF_Page_EndText")]
public Status end_text ();
[CCode(cname="HPDF_Page_TextOut")]
public Status text_out (Real x, Real y, string chars);
[CCode(cname="HPDF_Page_SetCharSpace")]
public Status set_char_space (Real value);
[CCode(cname="HPDF_Page_SetWordSpace")]
public Status set_word_space (Real value);
}
[Compact]
[CCode(cname="HPDF_Font")]
public class Font {
}
}
Then you can consume it from vala.
=== Filename: text_demo.vala ===
using HPDF;
public class Demos.TextDemo {
private void error_handler (Status error_no, Status detail_no) {
stderr.printf("Error %d - detail %d\n", (int)error_no, (int)detail_no);
}
public void run (string filename) {
string samp_text2 = "The quick brown fox jumps over the lazy dog.";
Doc pdf = new Doc(this.error_handler);
unowned Page page = pdf.add_page ();
unowned Font font;
font = pdf.get_font ("Helvetica");
page.set_font_and_size (font, 24);
/* char-spacing 0 */
page.begin_text ();
page.text_out (60, 140, samp_text2);
page.end_text ();
/* char-spacing 1.5 */
page.set_char_space (1.5f);
page.begin_text ();
page.text_out (60, 100, samp_text2);
page.end_text ();
/* char-spacing 1.5, word-spacing 3.5 */
page.set_word_space (2.5f);
page.begin_text ();
page.text_out (60, 60, samp_text2);
page.end_text ();
/* save the document to a file */
stderr.printf("Writing pdf to: %s\n", filename);
pdf.save_to_file (filename);
}
}
public static int main(string[] args) {
new Demos.TextDemo().run (args[1]);
return 0;
}
To compile (tried on windows you'll need to tweak the paths):
valac -C --save-temps text_demo.vala --vapidir . --pkg haru
gcc -g -o text_demo.exe text_demo.c -I ./libharu-2.0.8/include -L
libharu-2.0.8 -l libhpdf
-mms-bitfields -IC:/Src/Tools/opt/include/glib-2.0 -IC:/Src/Tools/opt/lib/glib-2.0/include -LC:/Src/Tools/opt/lib -lglib-2.0 -lintl -lgobject-2.0
And to run:
text_demo.exe test.pdf
I'm wanting to have a simple duck typing example in C# using dynamic objects. It would seem to me, that a dynamic object should have HasValue/HasProperty/HasMethod methods with a single string parameter for the name of the value, property, or method you are looking for before trying to run against it. I'm trying to avoid try/catch blocks, and deeper reflection if possible. It just seems to be a common practice for duck typing in dynamic languages (JS, Ruby, Python etc.) that is to test for a property/method before trying to use it, then falling back to a default, or throwing a controlled exception. The example below is basically what I want to accomplish.
If the methods described above don't exist, does anyone have premade extension methods for dynamic that will do this?
Example: In JavaScript I can test for a method on an object fairly easily.
//JavaScript
function quack(duck) {
if (duck && typeof duck.quack === "function") {
return duck.quack();
}
return null; //nothing to return, not a duck
}
How would I do the same in C#?
//C# 4
dynamic Quack(dynamic duck)
{
//how do I test that the duck is not null,
//and has a quack method?
//if it doesn't quack, return null
}
If you have control over all of the object types that you will be using dynamically, another option would be to force them to inherit from a subclass of the DynamicObject class that is tailored to not fail when a method that does not exist is invoked:
A quick and dirty version would look like this:
public class DynamicAnimal : DynamicObject
{
public override bool TryInvokeMember(InvokeMemberBinder binder, object[] args, out object result)
{
bool success = base.TryInvokeMember(binder, args, out result);
// If the method didn't exist, ensure the result is null
if (!success) result = null;
// Always return true to avoid Exceptions being raised
return true;
}
}
You could then do the following:
public class Duck : DynamicAnimal
{
public string Quack()
{
return "QUACK!";
}
}
public class Cow : DynamicAnimal
{
public string Moo()
{
return "Mooooo!";
}
}
class Program
{
static void Main(string[] args)
{
var duck = new Duck();
var cow = new Cow();
Console.WriteLine("Can a duck quack?");
Console.WriteLine(DoQuack(duck));
Console.WriteLine("Can a cow quack?");
Console.WriteLine(DoQuack(cow));
Console.ReadKey();
}
public static string DoQuack(dynamic animal)
{
string result = animal.Quack();
return result ?? "... silence ...";
}
}
And your output would be:
Can a duck quack?
QUACK!
Can a cow quack?
... silence ...
Edit: I should note that this is the tip of the iceberg if you are able to use this approach and build on DynamicObject. You could write methods like bool HasMember(string memberName) if you so desired.
Try this:
using System.Linq;
using System.Reflection;
//...
public dynamic Quack(dynamic duck, int i)
{
Object obj = duck as Object;
if (duck != null)
{
//check if object has method Quack()
MethodInfo method = obj.GetType().GetMethods().
FirstOrDefault(x => x.Name == "Quack");
//if yes
if (method != null)
{
//invoke and return value
return method.Invoke((object)duck, null);
}
}
return null;
}
Or this (uses only dynamic):
public static dynamic Quack(dynamic duck)
{
try
{
//invoke and return value
return duck.Quack();
}
//thrown if method call failed
catch (RuntimeBinderException)
{
return null;
}
}
Implementation of the HasProperty method for every IDynamicMetaObjectProvider WITHOUT throwing RuntimeBinderException.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Dynamic;
using Microsoft.CSharp.RuntimeBinder;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
namespace DynamicCheckPropertyExistence
{
class Program
{
static void Main(string[] args)
{
dynamic testDynamicObject = new ExpandoObject();
testDynamicObject.Name = "Testovaci vlastnost";
Console.WriteLine(HasProperty(testDynamicObject, "Name"));
Console.WriteLine(HasProperty(testDynamicObject, "Id"));
Console.ReadLine();
}
private static bool HasProperty(IDynamicMetaObjectProvider dynamicProvider, string name)
{
var defaultBinder = Binder.GetMember(CSharpBinderFlags.None, name, typeof(Program),
new[]
{
CSharpArgumentInfo.Create(
CSharpArgumentInfoFlags.None, null)
}) as GetMemberBinder;
var callSite = CallSite<Func<CallSite, object, object>>.Create(new NoThrowGetBinderMember(name, false, defaultBinder));
var result = callSite.Target(callSite, dynamicProvider);
if (Object.ReferenceEquals(result, NoThrowExpressionVisitor.DUMMY_RESULT))
{
return false;
}
return true;
}
}
class NoThrowGetBinderMember : GetMemberBinder
{
private GetMemberBinder m_innerBinder;
public NoThrowGetBinderMember(string name, bool ignoreCase, GetMemberBinder innerBinder) : base(name, ignoreCase)
{
m_innerBinder = innerBinder;
}
public override DynamicMetaObject FallbackGetMember(DynamicMetaObject target, DynamicMetaObject errorSuggestion)
{
var retMetaObject = m_innerBinder.Bind(target, new DynamicMetaObject[] {});
var noThrowVisitor = new NoThrowExpressionVisitor();
var resultExpression = noThrowVisitor.Visit(retMetaObject.Expression);
var finalMetaObject = new DynamicMetaObject(resultExpression, retMetaObject.Restrictions);
return finalMetaObject;
}
}
class NoThrowExpressionVisitor : ExpressionVisitor
{
public static readonly object DUMMY_RESULT = new DummyBindingResult();
public NoThrowExpressionVisitor()
{
}
protected override Expression VisitConditional(ConditionalExpression node)
{
if (node.IfFalse.NodeType != ExpressionType.Throw)
{
return base.VisitConditional(node);
}
Expression<Func<Object>> dummyFalseResult = () => DUMMY_RESULT;
var invokeDummyFalseResult = Expression.Invoke(dummyFalseResult, null);
return Expression.Condition(node.Test, node.IfTrue, invokeDummyFalseResult);
}
private class DummyBindingResult {}
}
}
impromptu-interface seems to be a nice Interface mapper for dynamic objects... It's a bit more work than I was hoping for, but seems to be the cleanest implementation of the examples presented... Keeping Simon's answer as correct, since it is still the closest to what I wanted, but the Impromptu interface methods are really nice.
The shortest path would be to invoke it, and handle the exception if the method does not exist. I come from Python where such method is common in duck-typing, but I don't know if it is widely used in C#4...
I haven't tested myself since I don't have VC 2010 on my machine
dynamic Quack(dynamic duck)
{
try
{
return duck.Quack();
}
catch (RuntimeBinderException)
{ return null; }
}
Have not see a correct answer here, MS provides an example now with casting to a dictionary
dynamic employee = new ExpandoObject();
employee.Name = "John Smith";
employee.Age = 33;
foreach (var property in (IDictionary<String, Object>)employee)
{
Console.WriteLine(property.Key + ": " + property.Value);
}
// This code example produces the following output:
// Name: John Smith
// Age: 33