I'm new to this SWIG thing and the interaction between Java and C++.
I'm trying using pjsip library wrapped with Java.
i have the following structure in a file called "call.hpp":
/**
* This structure contains parameters for Call::onCreateMediaTransportSrtp()
* callback.
*/
struct OnCreateMediaTransportSrtpParam
{
/**
* The media index in the SDP for which the SRTP media transport
* will be used.
*/
unsigned mediaIdx;
/**
* Specify whether secure media transport should be used. Application
* can modify this only for initial INVITE.
* Valid values are PJMEDIA_SRTP_DISABLED, PJMEDIA_SRTP_OPTIONAL, and
* PJMEDIA_SRTP_MANDATORY.
*/
pjmedia_srtp_use srtpUse;
/**
* Application can modify this to specify the cryptos and keys
* which are going to be used.
*/
vector<SrtpCrypto> cryptos;
};
now, this how the Java wrapper for this function looks like(i've omitted some getters and setters to reduce length):
package org.pjsip.pjsua2;
public class OnCreateMediaTransportSrtpParam {
private transient long swigCPtr;
protected transient boolean swigCMemOwn;
protected OnCreateMediaTransportSrtpParam(long cPtr, boolean cMemoryOwn) {
swigCMemOwn = cMemoryOwn;
swigCPtr = cPtr;
}
protected static long getCPtr(OnCreateMediaTransportSrtpParam obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected void finalize() {
delete();
}
public synchronized void delete() {
if (swigCPtr != 0) {
if (swigCMemOwn) {
swigCMemOwn = false;
pjsua2JNI.delete_OnCreateMediaTransportSrtpParam(swigCPtr);
}
swigCPtr = 0;
}
}
public void setCryptos(SWIGTYPE_p_vectorT_pj__SrtpCrypto_t value) {
pjsua2JNI.OnCreateMediaTransportSrtpParam_cryptos_set(swigCPtr, this, SWIGTYPE_p_vectorT_pj__SrtpCrypto_t.getCPtr(value));
}
public SWIGTYPE_p_vectorT_pj__SrtpCrypto_t getCryptos() {
long cPtr = pjsua2JNI.OnCreateMediaTransportSrtpParam_cryptos_get(swigCPtr, this);
return (cPtr == 0) ? null : new SWIGTYPE_p_vectorT_pj__SrtpCrypto_t(cPtr, false);
}
public OnCreateMediaTransportSrtpParam() {
this(pjsua2JNI.new_OnCreateMediaTransportSrtpParam(), true);
}
}
now, i don't know how to use setCryptos with the SWIGTYPE_p_vectorT_pj__SrtpCrypto_t type in the parameter.
i've trying the following:
SrtpCrypto crypto = new SrtpCrypto();
crypto.setName("AES_CM_128_HMAC_SHA1_80");
OnCreateMediaTransportSrtpParam prm = new OnCreateMediaTransportSrtpParam();
Vector<SrtpCrypto> cryptos = new Vector<SrtpCrypto>();
cryptos.add(crypto);
prm.setCryptos(cryptos);
but i get the following error:
error: incompatible types: Vector<SrtpCrypto> cannot be converted to SWIGTYPE_p_vectorT_pj_SrtpCrypto_t
any ideas on how to proceed?
Thank you very much!!!
From what you've shown there's a mismatch between the types SWIG sees and the types being used.
In call.hpp you have:
vector<SrtpCrypto>
This vector here is almost certainly one from namespace std, which you've told SWIG to wrap, but here because of namespace directives of some sort that you've not shown it the question SWIG hasn't realised they're the same type, and used a default opaque type instead which isn't interchangable.
The simplest fix is for you to change your header file to say:
std::vector<SrtpCrypto>
instead then the error will almost certainly go away.
Related
How do I use #CalledByNative("...")? I need a callback from a webrtc lib.
If you know the class PeerConnection on it:
PeerConnection.java
Here is a old version of PeerConnection but it's almost same now
I call the function addStream, but I can't get the callback from it.
Please explain how calledbynative works!
PeerConnection.java
/*
* Copyright 2013 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
package org.webrtc;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
/**
* Java-land version of the PeerConnection APIs; wraps the C++ API
* http://www.webrtc.org/reference/native-apis, which in turn is inspired by the
* JS APIs: http://dev.w3.org/2011/webrtc/editor/webrtc.html and
* http://www.w3.org/TR/mediacapture-streams/
*/
public class PeerConnection {
static {
System.loadLibrary("jingle_peerconnection_so");
}
/** Tracks PeerConnectionInterface::IceGatheringState */
public enum IceGatheringState { NEW, GATHERING, COMPLETE }
/** Tracks PeerConnectionInterface::IceConnectionState */
public enum IceConnectionState {
NEW,
CHECKING,
CONNECTED,
COMPLETED,
FAILED,
DISCONNECTED,
CLOSED
}
/** Tracks PeerConnectionInterface::SignalingState */
public enum SignalingState {
STABLE,
HAVE_LOCAL_OFFER,
HAVE_LOCAL_PRANSWER,
HAVE_REMOTE_OFFER,
HAVE_REMOTE_PRANSWER,
CLOSED
}
/** Java version of PeerConnectionObserver. */
public static interface Observer {
/** Triggered when the SignalingState changes. */
public void onSignalingChange(SignalingState newState);
/** Triggered when the IceConnectionState changes. */
public void onIceConnectionChange(IceConnectionState newState);
/** Triggered when the ICE connection receiving status changes. */
public void onIceConnectionReceivingChange(boolean receiving);
/** Triggered when the IceGatheringState changes. */
public void onIceGatheringChange(IceGatheringState newState);
/** Triggered when a new ICE candidate has been found. */
public void onIceCandidate(IceCandidate candidate);
/** Triggered when some ICE candidates have been removed. */
public void onIceCandidatesRemoved(IceCandidate[] candidates);
/** Triggered when media is received on a new stream from remote peer. */
public void onAddStream(MediaStream stream);
/** Triggered when a remote peer close a stream. */
public void onRemoveStream(MediaStream stream);
/** Triggered when a remote peer opens a DataChannel. */
public void onDataChannel(DataChannel dataChannel);
/** Triggered when renegotiation is necessary. */
public void onRenegotiationNeeded();
}
/** Java version of PeerConnectionInterface.IceServer. */
public static class IceServer {
public final String uri;
public final String username;
public final String password;
/** Convenience constructor for STUN servers. */
public IceServer(String uri) {
this(uri, "", "");
}
public IceServer(String uri, String username, String password) {
this.uri = uri;
this.username = username;
this.password = password;
}
public String toString() {
return uri + "[" + username + ":" + password + "]";
}
}
/** Java version of PeerConnectionInterface.IceTransportsType */
public enum IceTransportsType { NONE, RELAY, NOHOST, ALL }
/** Java version of PeerConnectionInterface.BundlePolicy */
public enum BundlePolicy { BALANCED, MAXBUNDLE, MAXCOMPAT }
/** Java version of PeerConnectionInterface.RtcpMuxPolicy */
public enum RtcpMuxPolicy { NEGOTIATE, REQUIRE }
/** Java version of PeerConnectionInterface.TcpCandidatePolicy */
public enum TcpCandidatePolicy { ENABLED, DISABLED }
/** Java version of PeerConnectionInterface.CandidateNetworkPolicy */
public enum CandidateNetworkPolicy { ALL, LOW_COST }
/** Java version of rtc::KeyType */
public enum KeyType { RSA, ECDSA }
/** Java version of PeerConnectionInterface.ContinualGatheringPolicy */
public enum ContinualGatheringPolicy { GATHER_ONCE, GATHER_CONTINUALLY }
/** Java version of PeerConnectionInterface.RTCConfiguration */
public static class RTCConfiguration {
public IceTransportsType iceTransportsType;
public List<IceServer> iceServers;
public BundlePolicy bundlePolicy;
public RtcpMuxPolicy rtcpMuxPolicy;
public TcpCandidatePolicy tcpCandidatePolicy;
public CandidateNetworkPolicy candidateNetworkPolicy;
public int audioJitterBufferMaxPackets;
public boolean audioJitterBufferFastAccelerate;
public int iceConnectionReceivingTimeout;
public int iceBackupCandidatePairPingInterval;
public KeyType keyType;
public ContinualGatheringPolicy continualGatheringPolicy;
public int iceCandidatePoolSize;
public boolean pruneTurnPorts;
public boolean presumeWritableWhenFullyRelayed;
public RTCConfiguration(List<IceServer> iceServers) {
iceTransportsType = IceTransportsType.ALL;
bundlePolicy = BundlePolicy.BALANCED;
rtcpMuxPolicy = RtcpMuxPolicy.NEGOTIATE;
tcpCandidatePolicy = TcpCandidatePolicy.ENABLED;
candidateNetworkPolicy = candidateNetworkPolicy.ALL;
this.iceServers = iceServers;
audioJitterBufferMaxPackets = 50;
audioJitterBufferFastAccelerate = false;
iceConnectionReceivingTimeout = -1;
iceBackupCandidatePairPingInterval = -1;
keyType = KeyType.ECDSA;
continualGatheringPolicy = ContinualGatheringPolicy.GATHER_ONCE;
iceCandidatePoolSize = 0;
pruneTurnPorts = false;
presumeWritableWhenFullyRelayed = false;
}
};
private final List<MediaStream> localStreams;
private final long nativePeerConnection;
private final long nativeObserver;
private List<RtpSender> senders;
private List<RtpReceiver> receivers;
PeerConnection(long nativePeerConnection, long nativeObserver) {
this.nativePeerConnection = nativePeerConnection;
this.nativeObserver = nativeObserver;
localStreams = new LinkedList<MediaStream>();
senders = new LinkedList<RtpSender>();
receivers = new LinkedList<RtpReceiver>();
}
// JsepInterface.
public native SessionDescription getLocalDescription();
public native SessionDescription getRemoteDescription();
public native DataChannel createDataChannel(String label, DataChannel.Init init);
public native void createOffer(SdpObserver observer, MediaConstraints constraints);
public native void createAnswer(SdpObserver observer, MediaConstraints constraints);
public native void setLocalDescription(SdpObserver observer, SessionDescription sdp);
public native void setRemoteDescription(SdpObserver observer, SessionDescription sdp);
public native boolean setConfiguration(RTCConfiguration config);
public boolean addIceCandidate(IceCandidate candidate) {
return nativeAddIceCandidate(candidate.sdpMid, candidate.sdpMLineIndex, candidate.sdp);
}
public boolean removeIceCandidates(final IceCandidate[] candidates) {
return nativeRemoveIceCandidates(candidates);
}
public boolean addStream(MediaStream stream) {
boolean ret = nativeAddLocalStream(stream.nativeStream);
if (!ret) {
return false;
}
localStreams.add(stream);
return true;
}
public void removeStream(MediaStream stream) {
nativeRemoveLocalStream(stream.nativeStream);
localStreams.remove(stream);
}
public RtpSender createSender(String kind, String stream_id) {
RtpSender new_sender = nativeCreateSender(kind, stream_id);
if (new_sender != null) {
senders.add(new_sender);
}
return new_sender;
}
// Note that calling getSenders will dispose of the senders previously
// returned (and same goes for getReceivers).
public List<RtpSender> getSenders() {
for (RtpSender sender : senders) {
sender.dispose();
}
senders = nativeGetSenders();
return Collections.unmodifiableList(senders);
}
public List<RtpReceiver> getReceivers() {
for (RtpReceiver receiver : receivers) {
receiver.dispose();
}
receivers = nativeGetReceivers();
return Collections.unmodifiableList(receivers);
}
public boolean getStats(StatsObserver observer, MediaStreamTrack track) {
return nativeGetStats(observer, (track == null) ? 0 : track.nativeTrack);
}
// Starts recording an RTC event log. Ownership of the file is transfered to
// the native code. If an RTC event log is already being recorded, it will be
// stopped and a new one will start using the provided file. Logging will
// continue until the stopRtcEventLog function is called. The max_size_bytes
// argument is ignored, it is added for future use.
public boolean startRtcEventLog(int file_descriptor, int max_size_bytes) {
return nativeStartRtcEventLog(file_descriptor, max_size_bytes);
}
// Stops recording an RTC event log. If no RTC event log is currently being
// recorded, this call will have no effect.
public void stopRtcEventLog() {
nativeStopRtcEventLog();
}
// TODO(fischman): add support for DTMF-related methods once that API
// stabilizes.
public native SignalingState signalingState();
public native IceConnectionState iceConnectionState();
public native IceGatheringState iceGatheringState();
public native void close();
public void dispose() {
close();
for (MediaStream stream : localStreams) {
nativeRemoveLocalStream(stream.nativeStream);
stream.dispose();
}
localStreams.clear();
for (RtpSender sender : senders) {
sender.dispose();
}
senders.clear();
for (RtpReceiver receiver : receivers) {
receiver.dispose();
}
receivers.clear();
freePeerConnection(nativePeerConnection);
freeObserver(nativeObserver);
}
private static native void freePeerConnection(long nativePeerConnection);
private static native void freeObserver(long nativeObserver);
private native boolean nativeAddIceCandidate(
String sdpMid, int sdpMLineIndex, String iceCandidateSdp);
private native boolean nativeRemoveIceCandidates(final IceCandidate[] candidates);
private native boolean nativeAddLocalStream(long nativeStream);
private native void nativeRemoveLocalStream(long nativeStream);
private native boolean nativeGetStats(StatsObserver observer, long nativeTrack);
private native RtpSender nativeCreateSender(String kind, String stream_id);
private native List<RtpSender> nativeGetSenders();
private native List<RtpReceiver> nativeGetReceivers();
private native boolean nativeStartRtcEventLog(int file_descriptor, int max_size_bytes);
private native void nativeStopRtcEventLog();
}
#CallByNative is an annotation used by the Google WebRtc Development team to specify mappings between Java and Native (C/C++) Stacks of WebRtc.
As said on there official WebRtc Chromium Git representation such as:
#CalledByNative is used by the JNI generator to create the necessary
JNI bindings and expose this method to native code.
Now if there is a question that how is it happening? How WebRtc Java code is inter-linked with WebRtc Native Code?
For this, I will suggest looking into the Native Code of WebRtc will help you most. You will learn more informative and relevant stuff from the Comments inside the native stack. On the other hand, you should also have the basic knowledge of
How Java Native Interface (JNI) works
How WebRtc Java and Native Stack is interrelated
To get detailed intuition of Java Native Interface, you can visit these references:
Java Native Interface Specification Contents
Java Native Interface Tips
Here I am giving you an example from PeerConnection.java. Let's say, we have a method inside PeerConnection.java class such as:
#CalledByNative("IceGatheringState")
static IceGatheringState fromNativeIndex(int nativeIndex) {
return values()[nativeIndex];
}
Now this method is mapped inside src/sdk/android/src/jni/pc/peer_connection.cc class such as:
static ScopedJavaLocalRef<jobject> JNI_PeerConnection_IceGatheringState(
JNIEnv* env,
const JavaParamRef<jobject>& j_pc) {
return Java_IceGatheringState_fromNativeIndex(
env, ExtractNativePC(env, j_pc)->ice_gathering_state());
}
If you follow the same technique by looking into both Java and Native Class of PeerConnection, you must found some other linkages of that kind. Now, what #CallByNative did here?
If you look, after loading library, #CallByNative basically do these two things such as:
Tells which inner class the method belongs to
Expose this Java method to the Native Code
Make JNI Bindings with Java and Native Code
These types of annotations are provided by Google Developers to make JNI easy and understandable while working with Pre-built libraries or your own compiled libraries such as libwebrtc.aar.
I did some investigation on this topic lately and I would like to share my results. I realized that it might have to do something with #Override methods and the "super" keyword in Java. I suppose when we try to call e.g. the override method onIceCandidate or onAddStream, which is located in the PeerConnection.java file, from the MainActivity.java class we write:
public class MainActivity extends AppCompatActivity implements View.OnClickListener, SignallingClient.SignalingInterface {
...
private void createPeerConnection() {
...
#Override
public void onIceCandidate(IceCandidate iceCandidate) {
super.onIceCandidate(iceCandidate);
onIceCandidateReceived(iceCandidate);
}
#Override
public void onAddStream(MediaStream mediaStream) {
showToast("Received Remote stream");
super.onAddStream(mediaStream);
gotRemoteStream(mediaStream);
}
...
}
...
}
Furthermore, the onAddStream and onIceCandidate are both override methods. If we trace those methods back by the help of the super keyword we get into the following file CustomPeerConnectionObserver.java:
class CustomPeerConnectionObserver implements PeerConnection.Observer {
...
#Override
public void onIceCandidate(IceCandidate iceCandidate) {
Log.d(logTag, "onIceCandidate() called with: iceCandidate = [" + iceCandidate + "]");
}
...
#Override
public void onAddStream(MediaStream mediaStream) {
Log.d(logTag, "onAddStream() called with: mediaStream = [" + mediaStream + "]");
}
...
}
At this point when we take a look at the class we see that it "implements" the PeerConnection.Observer. If we keep investigating a bit further, we realize that we are guided to our file PeerConnection.java:
public class PeerConnection {
...
public interface Observer {
...
#CalledByNative("Observer")
void onIceCandidate(IceCandidate var1);
...
#CalledByNative("Observer")
void onAddStream(MediaStream var1);
...
}
...
}
We get into the "interface" called Observer. So far I took the example from GitHub and I tried to comprehend how the application works. It is for Android, but I suppose we use Java, so there should not be a huge difference, correct me if I am wrong.
In conclusion, I suppose it somehow gets called automatically when a special event occurs and/or when it is mentioned/written as a #Override method, where we need it to be called. I also assume that the #CalledByNative tag could have impact on the method calls, i.e. addStream, also respectively in the example I provided above:
onIceCandidate
onAddStream.
I hope I was able to at least give you some reference points about my investigation on this behaviour, where you could pick up the trace and make further research. I am also looking forward that an expert could answer this good question on how #CalledByNative works.
In my program I receive a message from a call, that has a variable of the type Optional and depending on whether something is inside of this variable or not I shall make a call of a method with a parameter or a call to the same method without a parameter, standard overloading.
The problem I am having is that the produced code becomes ugly, especially the more optionals I receive the more distinguishing the method call becomes. Currently the next method call is determined via an if-else.
Here is a simplified code for this question, first the Message Class:
public class FooMessage {
public Optional<String> receivedMessage;
public FooMessage(String message) {
this.receivedMessage = Optional.ofNullable(message);
}
}
and then the Main class:
public class Main {
public static FooMessage receiveMessageWithNull(){
return new FooMessage(null);
}
public static FooMessage receiveMessage(String s){
return new FooMessage(s);
}
public static void fooMethod() {
System.out.println("Message == null");
}
public static void fooMethod(String message) {
System.out.println("Message != null");
}
public static void main(String[] args) {
//Calls that return a Message either with content or without
FooMessage message = receiveMessage("foo");
FooMessage messageWithNull = receiveMessageWithNull();
//Resolving which version of the overloaded method to call
if (message.receivedMessage.isPresent()) {
fooMethod(message.receivedMessage.get());
} else {
fooMethod();
}
if (messageWithNull.receivedMessage.isPresent()) {
fooMethod(messageWithNull.receivedMessage.get());
} else {
fooMethod();
}
}
}
My question is if there is a possibility to clean the code up in a way that the method call itself is written to resolve the checks currently done in the if statements. I was thinking about something like:
fooMethod(message.receivedMessage.isPresent() ? message.receivedMessage.get() : ... );
Instead of ... there would be something that told the method to ignore the parameter.
Note: I cannot change fooMethod. I have to resolve which version of fooMethod has to be called in the calling method.
If you need to execute the method only if the Optional value is present, and you do not care for an absent value, you may go with
message.receivedMessage.ifPresent(Main::fooMethod);
I would avoid passing the Optional to a method that then distinguishes whether the value is present, but you can implement a support function that would hide the implementation details
private static void distinguish(String s) {
if (s == null) fooMethod();
else fooMethod(s);
}
and cal it via
distinguish(message.receivedMessage.orElse(null));
This is an acceptable way of using Òptional::orElse.
From the docs:
/**
* Return the value if present, otherwise return {#code other}.
*
* #param other the value to be returned if there is no value present, may
* be null
* #return the value, if present, otherwise {#code other}
*/
public T orElse(T other) {
return value != null ? value : other;
}
I would personally just stick with if (optional.isPresent()) as this is what optionals are intended for, so I wouldn't worry too much.
I have on C side
typedef struct {} C_String;
C_String* StringNew();
void StringFree(C_String* string);
On Java I get such wrapper class
public class String {
private long swigCPtr;
protected boolean swigCMemOwn;
protected String(long cPtr, boolean cMemoryOwn) {
swigCMemOwn = cMemoryOwn;
swigCPtr = cPtr;
}
protected static long getCPtr(String obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
protected void finalize() {
delete();
}
public synchronized void delete() {
if (swigCPtr != 0) {
if (swigCMemOwn) {
swigCMemOwn = false;
JNI.delete_C_String(swigCPtr);
}
swigCPtr = 0;
}
}
public String() {
this(JNI.new_C_String(), true);
}
}
JNI.new_С_String and JNI.delete_С_String are native methods generated by SWIG, they do simple work - allocate C_String by malloc and delete it by free respectively. In my case scenario should be different, since C_String is empty structure and acts like shortcut proper way should be allocation by StringNew which has malloc under hood and freeing by StringFree.
I want to use proper methods instead of JNI.new_String, JNI.delete_String what the easiest method to achieve this?
Assuming the C you showed is in a file called test.h the following SWIG interface would do what you wanted:
%module test
%{
#include "test.h"
%}
%extend C_String {
C_String() {
return StringNew();
}
~C_String() {
StringFree($self);
}
}
%ignore StringNew;
%ignore StringFree;
%include "test.h"
This uses %extend to supply a custom constructor and destructor for the C_String type. This constructor/destructor pair just calls the C functions StringNew and StringFree respectively.
In your question you asked to have these calls happen from within the generated Java code. The way I've written it above makes these calls happen from within C instead. This has two primary benefits:
It is language neutral - the same interface file works equally well regardless of what language you're targeting.
It minimses calls between native and Java (or any other target) code. This is generally a good thing from a performance perspective since these cross-language jumps tend to be the most expensive part of your interface.
The remainder of this answer is mostly just here as a learning point and not a recommended solution.
If you really wanted to though you could write this as Java and make the JNI calls you asked for. To do this you would need to write a few typemaps, you would need at least two which (untested) might look something like:
javabody:
%typemap(javabody) C_String %{
private long swigCPtr;
protected boolean swigCMemOwn;
protected $javaclassname(long cPtr, boolean cMemoryOwn) {
swigCMemOwn = cMemoryOwn;
swigCPtr = cPtr;
}
public $javaclassname() {
$javaclassname tmp = $imclassname.StringNew();
swigCMemOwn = tmp.swigCMemoryOwn;
swigCPtr = tmp.swigCPtr;
tmp.swigCMemoryOwn = false;
}
protected static long getCPtr($javaclassname obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
%}
javadestruct:
%typemap(javabody) C_String %{
public synchronized void delete() {
if (swigCPtr != 0) {
if (swigCMemOwn) {
swigCMemOwn = false;
$imclassname.StringFree(swigCPtr);
}
swigCPtr = 0;
}
}
%}
But as noted previously this really isn't the best way to solve the problem. (You'd need a 'javadestruct_derived' typemap as well if you wanted this to still happen for cases with inheritance)
I am playing with functional programming and in particular with Functional Java. I have implemented with success my version of the IO Monad and I am writing IO actions for my core. It is basically serializing objects to Xml files (the object type extends the custom XmlWritable interface).
Unfortunately, in order to do that, an instance of OutputStream AND one instance of XmlSerializer needs to be created. The scope of the OutputStream is wider than XmlSerializer's, which means that the only way I can see to be able to correctly handle both lifecycles within my IO monad is to carry both of them with me in a tuple, closing OutputStream after having written using XmlSerializer.
This leads to heavy and ugly code (Java 6 is definitely not the best for this):
public abstract class IO<R> {
[...]
}
public class IOActions {
public final F<String, IO<OutputStream>> openFileFn() {
return new F<String, IO<OutputStream>>() {
#Override
public IO<OutputStream> f(String fileName) {
[...]
}
};
}
/* This will be partially applied, encoding will be fixed */
public static final F<OutputStream, IO<P2<OutputStream, XmlSerializer>>> initSerializer() {
return new F<OutputStream, IO<P2<OutputStream, XmlSerializer>>>() {
#Override
public IO<P2<OutputStream, XmlSerializer>> f(OutputStream os) {
XmlSerializer = new ...
[...]
}
};
}
/* This will be partially applied as well */
public static final F2<XmlWritable, P2<OutputStream, XmlSerializer>, IO<P2<OutputStream, XmlSerializer>>> writeObjectFn() {
return new F2<XmlWritable, P2<OutputStream, XmlSerializer>, IO<P2<OutputStream, XmlSerializer>>>() {
#Override
public IO<P2<OutputStream, XmlSerializer>> f(XmlWritable object, P2<OutputStream, XmlSerializer> p) {
[...]
}
};
}
Is there a more idiomatic why to handle my use case in functional programming?
Lurking, I discovered the State Monad...but I am kind of scared to see what it is going to happen if I apply a State Monad on top of a IO Monad in Functional Java.
I actually took great inspiration from Functional-Java's DB combinators to solve similar problems. I made my very own "XML combinators" (and more) from this pattern, so its worth learning.
You might find this discussion on google groups useful.
edit - replying to the comment:
follow the code:
notice how you start a new connection using the StateDb, see that you have a few options to start a connection, one that eventually commits, and one that eventually rollback. these are just two examples of things you can "carry" with the computation. Essentially, every computation that you bind (a plain modaic bind), could potentially carry information.
here is an example i gave in the discussion above:
DB<PreparedStatement> prepareStatement(final String sql) {
return new DB<PreparedStatement>() {
public PreparedStatement run(Connection c) throws SQLException {
return c.prepareStatement(sql);
}}}
// for a query that a reader might perform, i might have a function like this:
F<PreparedStatement, DB<ResultSet>> runStatement() {
public DB<ResultSet> f(final PreparedStatement s) {
return new DB<ResultSet>() {
public ResultSet run (Connection c) throws SQLException {
return s.executeQuery();
}}}
So in this example, you can pass extra information, namely the sql query as a parameter to the function that gets bound. you could just as well had more parameters to runStatement.
to put it all together, you get something like:
ResultSet rs = DbState.reader("conn-url").run(prepareStatement("select * from table").bind(runStatement());
Hope this helps!
Here is what I have come up with. Feedback is very appreciated.
I followed the answer above, taking inspiration from the linked discussion:
public class IOXml<T extends XmlWritable> implements DataWriter<T>{
private final XmlSerializer mXmlSerializer;
private final Option<String> mXmlEncoding;
private final IO<OutputStream> ioCreateStream;
private final F<OutputStream, IO<Unit>> ioCloseStream;
#Inject
IOXml(IO<OutputStream> createStream, F<OutputStream, IO<Unit>> closeStream, XmlSerializer xmlSerializer, Option<String> xmlEncoding) {
mXmlSerializer = xmlSerializer;
mXmlEncoding = xmlEncoding;
ioCreateStream = createStream;
ioCloseStream = closeStream;
}
/**
* Write a T object which is XmlWritable.
* #param osAndSer The tuple containing OutputStream and XmlSerializer.
* #param object The object to write.
* #return IO monad object.
*/
protected IO<Unit> writeObject(final T object) {
return new IO<Unit>() {
#Override
public Unit performIO() throws IOException {
object.writeXml(mXmlSerializer);
return Unit.unit();
}
};
}
protected final F<Unit, IO<Unit>> writeObjectFn(final T object) {
return new F<Unit, IO<Unit>>() {
#Override
public IO<Unit> f(Unit a) {
return writeObject(object);
}
};
}
/**
* Initialize the XmlSerializer before using it.
* #param os An OutputStream.
* #param encoding The encoding of the xml file.
* #return An IO action returning nothing.
*/
protected IO<Unit> initXml(final OutputStream os) {
return new IO<Unit>() {
#Override
public Unit performIO() throws IOException {
mXmlSerializer.setOutput(os, mXmlEncoding.toNull());
mXmlSerializer.startDocument(mXmlEncoding.toNull(), true);
return Unit.unit();
}
};
}
/**
* Close the XmlSerializer after.
* #return An IO action returning nothing.
*/
protected IO<Unit> closeXml() {
return new IO<Unit>() {
#Override
public Unit performIO() throws IOException {
mXmlSerializer.endDocument();
return Unit.unit();
}
};
}
protected final F<Unit, IO<Unit>> closeXmlFn() {
return new F<Unit, IO<Unit>>() {
#Override
public IO<Unit> f(Unit a) {
return closeXml();
}
};
}
#Override
public void close() throws IOException {
closeXml().performIO();
}
#Override
public void write(T object) {
throw new UnsupportedOperationException("Are you sure? IOXml is a functional class. Use the function returned by liftIO instead.");
}
/**
* Curried function to write XML objects, given the object itself and an OutputStream.
* #return The curried function.
*/
protected F<OutputStream, F<T, IO<Unit>>> writeFn() {
// returning the outer
return new F<OutputStream, F<T, IO<Unit>>>() {
#Override
public F<T, IO<Unit>> f(final OutputStream os) {
// Returning the inner
return new F<T, IO<Unit>>() {
#Override
public IO<Unit> f(T object) {
return initXml(os).bind(writeObjectFn(object)).bind(closeXmlFn());
}
};
}
};
}
#Override
public IO<Unit> writeIO(final T object) {
return IOImpl.bracket(ioCreateStream, // init
ioCloseStream, // close
Function.partialApply2(writeFn(), object)); // body
}
}
I have used typemaps :
%include "typemaps.i"
%apply SWIGTYPE * {char *deci};
This is the proxy class generated by SWIG:
public class SWIGTYPE_p_char {
private long swigCPtr;
protected SWIGTYPE_p_char(long cPtr, boolean futureUse) {
swigCPtr = cPtr;
}
protected SWIGTYPE_p_char() {
swigCPtr = 0;
}
protected static long getCPtr(SWIGTYPE_p_char obj) {
return (obj == null) ? 0 : obj.swigCPtr;
}
}
it seems trivial, but have you tried:
SWIGTYPE_p_char my_p_char;
after all, all that the class contains is a long int representation of the pointer address
clearly you can only use this for a c function that returns a char* or uses a char* parameter as a pseudo return value
if you want to use a char* input parameter then you'll most likely have to write a helper function of some sort
I wrapped this function prototype with SWIG recently:
int load_config(const char *fn, Config *cfg);
which loaded data into my Config data-structure from a file specified by a string (char*)
I was able to call it from Java with the following line:
example.load_config("test.cfg", cfg);
or alternatively:
String cfg_file = "test.cfg";
example.load_config(cfg_file, cfg);