how to create a php extension for an object? - c++

I am working on a php extension for c++ classes. How to create a link to a method that accepts as parameter an object of a class?
Can you give me some examples?
THX. APPRECIATE!
I succedded to create a link to a method that accepts as parameter a string or int. But I don't know how to do this for a method.
Here is a short example:
PHP_METHOD(Class1, method_string)
{
Class1 *access;
char *strr=NULL;
int strr_len;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &strr, &strr_len) == FAILURE) {
RETURN_NULL();
}
access_object *obj = (access_object *)zend_object_store_get_object(
getThis() TSRMLS_CC);
access = obj->access;
if (access != NULL) {
std::string s(strr);
RETURN_BOOL(access->method_string(s));
}
}

Use the zend API zend_parse_method_parameters():
ZEND_METHOD(ext_access_class, do_something)
{
zval* objid_this = NULL, objid1 = NULL;
// note: ext_access_class_entry and ext_param_class_entry are of type zend_class_entry*
if (zend_parse_method_parameters(ZEND_NUM_ARGS() TSRMLS_CC, getThis(), "OO", &objid_this, ext_access_class_entry, &objid1, ext_param_class_entry) == FAILURE)
RETURN_NULL();
ext_access_class* const access_obj = (ext_access_class*) zend_object_store_get_object(objid_this TSRMLS_CC);
Class1* const access = access_obj->access;
ext_param_class* const param_obj = (ext_param_class*) zend_object_store_get_object(objid1 TSRMLS_CC);
Class2* const myobject = param_obj->myobject;
const bool ret = access->do_something(myobject);
RETURN_BOOL(ret);
}

I believe ZEND_API int zend_parse_method_parameters(int num_args TSRMLS_DC, zval *this_ptr, char *type_spec, ...); AND
ZEND_API int zend_parse_method_parameters_ex(int flags, int num_args TSRMLS_DC, zval *this_ptr, char *type_spec, ...); are the right API for retrieving the input parameters in the method.
I think the same API will help you accept an object as an input parameter.

Related

Need an example using dht_put_item with libtorrent

I'm trying to use dht to keep mutable data with libtorrent. As far as I can understand, the right way is to use the method dht_put_item from session. The problem is that I need to pass a callback function and I don't know what I'm doing wrong... my code looks like the following
namespace lt = libtorrent;
//The callback function
void cb(lt::entry& cdentry, boost::array<char,64>& cbarray, boost::uint64_t& cbint, std::string const& cbstring){
//My stuff here
}
void main(){
//The session
lt::session ses;
//The data I want to insert into DHT
std::string cadenaStr = "519d818411de49652b4aaf34850321de28bb2dce";
//Now I create the keys
unsigned char seed[32];
unsigned char public_key[32];
unsigned char private_key[64];
unsigned char signature[32];
ed25519_create_seed(seed);
ed25519_create_keypair(public_key, private_key, seed);
ed25519_sign(signature, cadenaStr.c_str(), sizeof(cadenaStr.c_str()), public_key, private_key);
//How can I use this?, where is the data supposed to go? :|
ses.dht_put_item(public_key, cb, false);
}
On libtorrent/session_handler.hpp this method is defined as
void dht_put_item(boost::array<char, 32> key
, boost::function<void(entry&, boost::array<char,64>&
, boost::uint64_t&, std::string const&)> cb
, std::string salt = std::string());
Can someone please tell me what I'm doing wrong.
Thanks!
There is an example in the libtorrent repository that I use for testing. It can generate keys, put and get both mutable and immutable items.
https://github.com/arvidn/libtorrent/blob/master/tools/dht_put.cpp
How can I use this?, where is the data supposed to go? :|
You provide the data in the callback that's called. The reason for this kind of API is that there are use cases where you want to mutate the data, and then you need to first know whether something is already stored under this key, and what it is.
You are missing the settings pack for your session.
lt::settings_pack settings;
settings.set_bool(settings_pack::enable_dht, false);
settings.set_int(settings_pack::alert_mask, 0xffffffff);
ses.apply_settings(settings);
settings.set_bool(settings_pack::enable_dht, true);
ses.apply_settings(settings);
Then you need to wait until you receive a boostrap message by waiting for an alert.
wait_for_alert(ses, dht_bootstrap_alert::alert_type);
Last, your dht_put_item call:
char const* cadenaStr = "519d818411de49652b4aaf34850321de28bb2dce";
dht_put_item(public_key, std::bind(&put_string, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3, std::placeholders::_4, public_key, private_key, cadenaStr));
You will need these functions:
static alert* wait_for_alert(session* ses, int alert_type)
{
alert* ret = nullptr;
bool found = false;
while (!found)
{
ses->wait_for_alert(seconds(5));
std::vector<alert*> alerts;
ses->pop_alerts(&alerts);
for (std::vector<alert*>::iterator i = alerts.begin()
, end(alerts.end()); i != end; ++i)
{
if ((*i)->type() != alert_type)
{
continue;
}
ret = *i;
found = true;
}
}
return ret;
}
static void put_string(
entry& e
,boost::array<char, 64>& sig
,boost::int64_t& seq
,std::string const& salt
,boost::array<char, 32> const& pk
,boost::array<char, 64> const& sk
,char const* str)
{
using dht::sign_mutable_item;
if (str != NULL) {
e = std::string(str);
std::vector<char> buf;
bencode(std::back_inserter(buf), e);
dht::signature sign;
seq++;
sign = sign_mutable_item(buf, salt, dht::sequence_number(seq)
, dht::public_key(pk.data())
, dht::secret_key(sk.data()));
sig = sign.bytes;
}
}

Implementing callback function within a class in C++

I would like to implement a workaround to use a non-static class as a call-back function. I am working with Eclipse Paho MQTT code. The following type is implemented and used as callback:
typedef void MQTTAsync_onSuccess(void* context, MQTTAsync_successData* response);
MQTTAsync_onSuccess* onSuccess;
onSuccess = myStaticCallback;
void myStaticCallback (void* context, MQTTAsync_successData* response)
{
//...callback actions...
}
I want to wrap this C API (without modifying the existing MQTT C API) and implement non-static / non-centralized callback function that belongs to an object/class.
typedef void MQTTAsync_onSuccess(void* context, MQTTAsync_successData* response);
class myMQTTClass
{
private:
void myCallback (void* context, MQTTAsync_successData* response);
MQTTAsync_onSuccess* onSuccess;
public:
void foo (void)
{
this->onSuccess = this->myCallback;
}
}
As you might guess, the code above causes the error:
cannot convert myCallback from type 'void (myMQTTClass::) (void*, MQTTAsync_successData*)' to type 'void (*)(void*, MQTTAsync_successData*)'.
Any guidance as to how to address this issue or any workaround is greately appreciated. I would be willing to provide any possible missing information. Thanks in advance.
EDIT: Actual code with some omissions
namespace rover
{
typedef struct
{
char * clientID;
char * topic;
char * payload;
int qos; // 1
long int timeout; // Such as 10000L usec
} RoverMQTT_Configure_t;
class RoverPahoMQTT
{
public:
RoverPahoMQTT (char * host_name, int port, RoverMQTT_Configure_t MQTT_Configure);
private:
/**
* #brief Host name used for connecting to the Eclipse Paho MQTT server
*/
char * HOST_NAME;
/**
* #brief Port used for connecting to the Eclipse Paho MQTT server
*/
int PORT;
RoverMQTT_Configure_t rover_MQTT_configure;
/* Internal attributes */
MQTTAsync client;
/**
* #brief Connect options
*/
MQTTAsync_connectOptions conn_opts;
/**
* #brief Disconnect options
*/
MQTTAsync_disconnectOptions disc_opts;
//...
static void onPublisherConnect (void* context, MQTTAsync_successData* response);
void onPublisherConnect_ (MQTTAsync_successData* response);
//...
}
}
int rover::RoverPahoMQTT::publish (void)
{
this->flushFlags ();
this->conn_opts = MQTTAsync_connectOptions_initializer;
this->client = new MQTTAsync;
int rc;
char my_addr[20];
this->constructAddress (my_addr);
printf ("address: %s", my_addr);
MQTTAsync_create ( &(this->client),
my_addr,
this->rover_MQTT_configure.clientID,
MQTTCLIENT_PERSISTENCE_NONE,
NULL);
MQTTAsync_setCallbacks(this->client, NULL, onConnectionLost, NULL, NULL);
conn_opts.keepAliveInterval = 20;
conn_opts.cleansession = 1;
conn_opts.onSuccess = rover::RoverPahoMQTT::onPublisherConnect;
conn_opts.onFailure = onConnectFailure;
conn_opts.context = this->client;
if ((rc = MQTTAsync_connect(this->client, &(this->conn_opts))) != MQTTASYNC_SUCCESS)
{
printf("Failed to start connect, return code %d\n", rc);
return rc;
}
/*printf("Waiting for publication of %s\n"
"on topic %s for client with ClientID: %s\n",
PAYLOAD, TOPIC, CLIENTID);*/
while (!mqtt_finished)
usleep(this->rover_MQTT_configure.timeout);
MQTTAsync_destroy(&client);
return rc;
}
void rover::RoverPahoMQTT::onPublisherConnect_(MQTTAsync_successData* response)
{
MQTTAsync_responseOptions opts = MQTTAsync_responseOptions_initializer;
MQTTAsync_message pubmsg = MQTTAsync_message_initializer;
int rc;
printf("Successful connection\n");
opts.onSuccess = onPublisherSend;
opts.context = client;
pubmsg.payload = &default_MQTT_configure.payload;
pubmsg.payloadlen = strlen(default_MQTT_configure.payload);
pubmsg.qos = default_MQTT_configure.qos;
pubmsg.retained = 0;
deliveredtoken = 0;
if ((rc = MQTTAsync_sendMessage(client, default_MQTT_configure.topic, &pubmsg, &opts)) != MQTTASYNC_SUCCESS)
{
printf("Failed to start sendMessage, return code %d\n", rc);
exit(EXIT_FAILURE);
}
}
void rover::RoverPahoMQTT::onPublisherConnect (void* context, MQTTAsync_successData* response)
{
rover::RoverPahoMQTT* m = (rover::RoverPahoMQTT*) context;
m->onPublisherConnect_(response);
//((rover::RoverPahoMQTT*)context)->onPublisherConnect_(response);
// ^^^HERE IS THE SEGMENTATION FAULT
}
As clearly stated here, the callback has to be
registered with the client library by passing it as an argument in
MQTTAsync_responseOptions
and the context argument is a
pointer to the context value originally passed to
MQTTAsync_responseOptions, which contains any application-specific
context.
I suggest a common interface for your classes, which provides a static method that matches the callback prototype:
class myMQTTClass
{
public:
static void callback(void* context, MQTTAsync_successData* response)
{
myMQTTClass * m = (myMQTTClass*)context;
m->myCallback(response);
}
protected:
virtual void myCallback(MQTTAsync_successData* response) = 0;
};
You can now implement different behaviours in subclasses:
class myMQTTClassImpl : public myMQTTClass
{
protected:
void myCallback(MQTTAsync_successData *response)
{
std::cout << "success!!!" << std::endl;
}
};
Let's see how to use it:
int main()
{
myMQTTClass * m = new myMQTTClassImpl();
MQTTAsync_responseOptions options;
options.onSuccess = myMQTTClass::callback;
options.context = m;
}
Edit (refers to actual code posted):
In your publish method, this is right:
conn_opts.onSuccess = rover::RoverPahoMQTT::onPublisherConnect;
this is wrong:
conn_opts.context = this->client;
it should be:
conn_opts.context = this;
If you can use the context parameter to point to the object you want the callback to fire on you could simply make the callback function static and forward to an instance function. If the context parameter is needed for other data then I would use libffi to generate a closure and associate the object pointer with the closure's saved arguments.
There is no correct way to pass an actual instance function to that callback and be sure that it will work (even if you made the instance function be something like void MyCallback(MQTTAsync_successData*) and then forcibly cast that to the callback type you aren't guaranteed that the calling convention would match.
For the first (where you can use the context arg to point to 'this'):
class MyCallback
{
static void CallbackFunc(void * ptr, MQTTAsync_successData* data)
{
((MyCallback*)ptr)->RealCallback(data);
}
void RealCallback(MQTTAsync_successData*)
{}
};
You would then assign &MyCallback::CallbackFunc to the function pointer.
libffi is quite a bit more complicated.

How to get a Java string from a char pointer in C++

I am porting the openvr sample to jogl, after we created the binding with jna.
Almost at the end (before rendering the controllers and the tracking base stations), I got stuck trying to translate a char pointer in C to a String in Java.
C++ code here:
//-----------------------------------------------------------------------------
// Purpose: Helper to get a string from a tracked device property and turn it
// into a std::string
//-----------------------------------------------------------------------------
std::string GetTrackedDeviceString( vr::IVRSystem *pHmd, vr::TrackedDeviceIndex_t unDevice, vr::TrackedDeviceProperty prop, vr::TrackedPropertyError *peError = NULL )
{
uint32_t unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, NULL, 0, peError );
if( unRequiredBufferLen == 0 )
return "";
char *pchBuffer = new char[ unRequiredBufferLen ];
unRequiredBufferLen = pHmd->GetStringTrackedDeviceProperty( unDevice, prop, pchBuffer, unRequiredBufferLen, peError );
std::string sResult = pchBuffer;
delete [] pchBuffer;
return sResult;
}
GetStringTrackedDeviceProperty here:
/** Returns a string property. If the device index is not valid or the property is not a string type this function will
* return 0. Otherwise it returns the length of the number of bytes necessary to hold this string including the trailing
* null. Strings will generally fit in buffers of k_unTrackingStringSize characters. */
virtual uint32_t GetStringTrackedDeviceProperty( vr::TrackedDeviceIndex_t unDeviceIndex, ETrackedDeviceProperty prop, VR_OUT_STRING() char *pchValue, uint32_t unBufferSize, ETrackedPropertyError *pError = 0L ) = 0;
Where VR_OUT_STRING() is defined here as:
# define VR_CLANG_ATTR(ATTR)
#define VR_OUT_STRING() VR_CLANG_ATTR( "out_string: ;" )
I have already done something similar where I had to call a function that expect the pointer to an array of TrackedDevicePose_t structures:
private TrackedDevicePose_t.ByReference trackedDevicePosesReference = new TrackedDevicePose_t.ByReference();
public TrackedDevicePose_t[] trackedDevicePose
= (TrackedDevicePose_t[]) trackedDevicePosesReference.toArray(VR.k_unMaxTrackedDeviceCount);
I created first the reference and then from it the actual array.
But here I can't have a class extending the char array..
private String getTrackedDeviceString(IVRSystem hmd, int device, int prop, IntBuffer propError) {
int requiredBufferLen = hmd.GetStringTrackedDeviceProperty.apply(device, prop, Pointer.NULL, 0, propError);
if(requiredBufferLen == 0) {
return "";
}
CharArray.ByReference charArrayReference = new CharArray.ByReference();
char[] cs = charArrayReference.toArray(requiredBufferLen);
return null;
}
Where apply (here) is:
public interface GetStringTrackedDeviceProperty_callback extends Callback {
int apply(int unDeviceIndex, int prop, Pointer pchValue, int unBufferSize, IntBuffer pError);
};
CharArray class, crap attempt here
Any ideas?
I've done some porting of C and C++ code to Java, and while it's probably horribly hacky, the best I've come up with to solve cases where a pointer to an int primitive or a char*/String is needed for a function call, is to create a small wrapper class with a single property, pass that object into the function, change the property as needed, and retrieve the new value after the function call. So something like:
public class StringPointer {
public String value = "";
}
StringPointer pchBuffer = new StringPointer();
unRequiredBufferLen = pHmd.GetStringTrackedDeviceProperty( unDevice, prop, pchBuffer, unRequiredBufferLen, peError );
String sResult = pchBuffer.value;
and inside GetStringTrackedDeviceProperty()
...
pchValue.value = "some string";
...
In this case, you can use a String, since that's what your code is doing with the char* after the function call, but if it actually really needs to be a char[], you can just create char[] pchBuffer = new char[unRequiredBufferLen]; and pass that into the function. It will be just like you were using a char* in C++, and any changes you make inside the array will be visible after the function ends, and you can even do String sResult = new String(pchBuffer);.

Get the variable values at runtime using reflection in Dlang

Is it possible to get the class/struct/other variables value during runtime in dlang to get/set its value? If yes how to do that please provide example.
And also is it possible to get the runtime variable value?
Ex:
class S{ int svariable = 5;}
class B { int bvariable = 10;}
void printValue(T, T instanceVariable, string variableName) {
writeln("Value of ", variableName, "=", instanceVariable.variableName);
}
Output:
Value of svariable = 5;
Value of bvariable = 10;
There is a library named witchcraft that allows for runtime reflection. There are examples of how to use it on that page.
I'd first recommend trying a reflection library like #mitch_ mentioned. However, if you want to do without an external library, you can use getMember to get and set fields as well as invoke functions:
struct S {
int i;
int fun(int val) { return val * 2; }
}
unittest {
S s;
__traits(getMember, s, "i") = 5; // set a field
assert(__traits(getMember, s, "i") == 5); // get a field
assert(__traits(getMember, s, "fun")(12) == 24); // call a method
}

How to write and read Stream using indy 10.5.5 c++

Hi I have try to read Stream from the server with this code
void __fastcall TForm1::Edit1KeyPress(TObject *Sender, wchar_t &Key)
{
//TMemoryStream *TMS = new TMemoryStream;
TStringStream *TSS = new TStringStream;
AnsiString A,B;
TStream *TS;
INT64 Len;
try
{
if (Key == VK_RETURN)
{
Beep(0,0);
if(Edit1->Text == "mystream")
{
TCPClient1->IOHandler->WriteLn("mystream");
Len = StrToInt(TCPClient1->IOHandler->ReadLn());
TCPClient1->IOHandler->ReadStream(TS,Len,false);
TSS->CopyFrom(TS,0);
RichEdit1->Lines->Text = TSS->DataString;
Edit1->Clear();
}
else
{
TCPClient1->IOHandler->WriteLn(Edit1->Text);
A = TCPClient1->IOHandler->ReadLn();
RichEdit1->Lines->Add(A);
Edit1->Clear();
}
}
}
__finally
{
TSS->Free();
}
}
and every times client try to read stream from the server, compiler says.
First chance exception at $75D89617. Exception class EAccessViolation with message 'Access violation at address 500682B3 in module 'rtl140.bpl'. Read of address 00000018'. Process Project1.exe (6056)
How to handle this?
You are not instantiating your TStream object before calling ReadStream(). Your TS variable is completely uninitialized. ReadStream() does not create the TStream object for you, only writes to it, so you have to create the TStream yourself beforehand.
Given the code you have shown, you can replace the TStream completely by using the ReadString() method instead:
void __fastcall TForm1::Edit1KeyPress(TObject *Sender, wchar_t &Key)
{
if (Key == VK_RETURN)
{
Beep(0,0);
if (Edit1->Text == "mystream")
{
TCPClient1->IOHandler->WriteLn("mystream");
int Len = StrToInt(TCPClient1->IOHandler->ReadLn());
RichEdit1->Lines->Text = TCPClient1->IOHandler->ReadString(Len);
}
else
{
TCPClient1->IOHandler->WriteLn(Edit1->Text);
String A = TCPClient1->IOHandler->ReadLn();
RichEdit1->Lines->Add(A);
}
Edit1->Clear();
}
}