I am a beginner in the world of swig/python and was trying to access an array of C-structure in python, but I get the following error:
TypeError: 'bar' object does not support indexing
Here is a simplified version of what I am trying to do:
foo.h:
#include <inttypes.h>
typedef struct bar {
uint8_t val;
}bar;
typedef struct foo {
union {
bar b[2];
} u;
}foo;
int fill_foo(foo *);
foo.c:
#include "foo.h"
int
fill_foo(foo *var)
{
var->u.b[0].val = 10;
var->u.b[1].val = 20;
return 0;
}
foo_test.i:
%module foo_test
%{
#include "foo.h"
%}
%include "foo.h"
foo.py:
import foo_test
f = foo_test.foo()
foo_test.fill_foo(f)
print f.u.b[0]
I have read a few other posts about c-arrays and swig, but its not clear to me how can i can solve this particular case. Would be glad if someone can help me out.
After some poking around I figured out that I need to extend struct bar to fix the issue. I was able to get my above sample code working with the following addition to foo_test.i.
%extend bar {
const bar __getitem__(int i) {
return $self[i];
}
}
The one downside though is that I need to add such an extension for every structure that gets used as array. Still haven't figured out how to solve this generically for all array of structures.
Related
need help with styling.
c_code = """
//This is an auto-generated file, please don't edit.
#include <stdio.h>
int main(){
return 0;
}
"""
file = open('main.c', 'w')
file.write(c_code)
file.close()
I have this simple code that will generate a c file from a python string. Is there a way to write the code after formatting in c style to the file? Instead of me explicitly indenting all code in the string?
So instead of this:
//This is an auto-generated file, please don't edit.
#include <stdio.h>
int main(){
return 0;
}
I need this:
//This is an auto-generated file, please don't edit.
#include <stdio.h>
int main() {
return 0;
}
Is there a library that can do this? Thanks in advance.
I am using an Apache Ignite docker container and I want to write data to the cache using C++ and then access it using Pyignite in Python. I have been able to add key-value pairs to the cache in C++ using the Apache Ignite C++ Thin Client and then read them from Python using Pyignite if they key and value are recognized types in both languages, e.g. int, float, string, etc. I want to create key-value pairs where the key is a std::string and the value is a std::map, but I couldn't get that to work in C++, so I decided to follow the directions here to create my C++ struct containing a std::map. I can get/put key-value pairs into the cache in C++, but I haven't been able to get them using Python.
Here's the C++ struct that works:
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <ignite/binary/binary.h>
#include <ignite/thin/ignite_client.h>
#include <ignite/thin/cache/cache_client.h>
using namespace ignite;
using namespace thin;
using namespace cache;
class MyMap
{
friend struct binary::BinaryType<MyMap>;
public:
MyMap() { }
MyMap(std::map<std::string, float> mp) :
mp(mp) { }
std::map<std::string, float> GetMap() const
{
return mp;
}
std::string ToString()
{
std::ostringstream oss;
for(std::map<std::string, float>::const_iterator it = mp.begin();
it != mp.end(); ++it)
{
oss << it->first << ": " << it->second << "\n";
}
return oss.str();
}
std::map<std::string, float> mp;
};
typedef struct MyMap MyMap;
template<>
struct binary::BinaryType<MyMap>
{
static int32_t GetTypeId()
{
return GetBinaryStringHashCode("MyMap");
}
static void GetTypeName(std::string& name)
{
name = "MyMap";
}
static int32_t GetFieldId(const char* name)
{
return GetBinaryStringHashCode(name);
}
static bool IsNull(const MyMap& obj)
{
return obj.GetMap().empty();
}
static void GetNull(MyMap& mymap)
{
mymap = MyMap();
}
static void Write(BinaryWriter& writer, const MyMap& obj)
{
BinaryMapWriter<std::string, float> bmap_writer = writer.WriteMap<std::string, float>("mp");
for(std::map<std::string, float>::const_iterator it = obj.mp.begin(); it != obj.mp.end(); ++it)
{
bmap_writer.Write(it->first, it->second);
}
bmap_writer.Close();
}
static void Read(BinaryReader& reader, MyMap& mymap)
{
BinaryMapReader<std::string, float> bmap_reader = reader.ReadMap<std::string, float>("mp");
std::string key;
float val;
std::map<std::string, float> tmp_map;
while (bmap_reader.HasNext()){
bmap_reader.GetNext(key, val);
tmp_map[key] = val;
}
mymap.mp = tmp_map;
}
};
And here's how I populate the cache:
IgniteClientConfiguration cfg;
cfg.SetEndPoints("ignite_cache");
IgniteClient client = IgniteClient::Start(cfg);
CacheClient<std::string, MyMap> cache = client.GetOrCreateCache<std::string, MyMap>("ignite_cache");
std::string key("my_test_key");
std::map<std::string, float> data_map;
data_map["a"] = 1.0;
data_map["b"] = 2.5;
MyMap mymap (data_map);
cache.Put(key, mymap);
Here's what I'm trying to get working, but haven't been able to in Python:
import pyignite
client = pyignite.Client()
client.connect("ignite_cache", 10800)
my_cache = client.get_or_create_cache("ignite_cache")
key = "my_test_key"
# This returns True
my_cache.contains_key(key)
# I was unable to get pyignite.register_binary_type to work so I tried this
res = pyignite.api.binary.put_binary_type(client, "MyMap", schema={'mp': pyignite.datatypes.MapObject})
# res.message = 'Success'
# This is where it doesn't work - it doesn't break, it just hangs.
my_cache.get(key)
I think my issue is how I am trying to access the cache in Python. If the value is an integer, float, string, or something built-in, then I can do cache.get(key) in Python and it works. And since the C++ code works for both cache.Get and cache.Put (in C++), I think it's something about going between the two languages.
Does anyone have an idea of what I'm doing wrong or how I need to modify either the C++ struct or how I define the binary type in Python?
I'm not sure why it hangs, but try playing around with the client's compact_footer parameter, it might be needed for C++ node.
You may also scan the node's console log for the clues.
As a side note, you don't have to register any types here. Just my_cache.get(key) should be enough.
Assume I've got a simple structure with a single field:
typedef struct {
MY_UNICODE value[512];
} TEST_STRUCTURE
Where MY_UNICODE is a custom unicode implementation.
Additionally I've got two methods:
int UTF8ToMyUnicode(char *utf8, MY_UNICODE *unicode);
int MyUnicodeToUTF8(MY_UNICODE *unicode, char *utf8);
To convert from and to this custom type.
Now I can generate a Python interface for this using SWIG.
But when I try to access TESTSTRUCTURE.value in Python. I always get a point to an MY_UNICODE object.
My question is: How do I wrap the access to the member of the struct such that I do get python strings and can set the value using python strings?
I know the documentation of SWIG says something about the memberin typemap.
But my example does not work:
%module test
%include "typemaps.i"
// This is the header file, where the structure and the functions are defined
%include "test.h"
%typemap(memberin) MY_UNICODE [512] {
if(UTF8ToMyUnicode($1, $input) != 0) {
return NULL;
}
}
%typemap(memberout) MY_UNICODE [512] {
if(MyUnicodeToUTF8($1, $input) != 0) {
return NULL;
}
}
In the generated wrapper file, the map has not been applied.
Any help would be appreciated! Thanks!
PS: I'm using swig 2.0.10
I've solved the problem myself. The important thing is that the typemaps need to be defined BEFORE the structures are defined in the interface. The documentation is not clear about that (or I have not seen it). Additionally the "in" and "out" typemaps can than be used to transform the values.
This example works for me:
%module test
%include "typemaps.i"
%typemap(in) MY_UNICODE [512] {
if(UTF8ToMyUnicode($1, $input) != 0) {
return NULL;
}
}
%typemap(out) MY_UNICODE [512] {
if(MyUnicodeToUTF8($1, $result) != 0) {
return NULL;
}
}
// Now include the header file
%include "test.h"
I have these two classes:
typedef std::vector<Entity *> EntityPtrVector;
class A
{
private:
EntityPtrVector entity_vector;
public:
void AddEntity(Entity *);
void RemoveEntity(std::string);
};
class Entity
{
private:
std::string name_;
public:
Entity();
Entity(std::string);
std::string GetName(void) const { return name_; }
void SetName(const std::string& name) { name_ = name; }
};
I expose them by boost::python like this:
BOOST_PYTHON_MODULE(my_lib)
{
using namespace boost::python;
class_<EntityPtrVector>("EntityPtrVector")
.def(vector_indexing_suite<EntityPtrVector>());
class_<A>("A", init<std::string>())
.def("AddEntity", &A::AddEntity)
.def("RemoveEntity", &A::RemoveEntity)
;
class_<Entity>("Entity", init<std::string>())
.add_property("name", &Entity::GetName, &Entity::SetName)
;
}
The implementations of AddEntity and RemoveEntity are:
void Game::AddEntity(Entity *E)
{
entity_vector.push_back(E);
}
void Game::RemoveEntity(std::string entity_name)
{
EntityPtrVector::iterator entity_ptr;
// Find the entity with the input name
for(entity_ptr = entity_vector.begin(); entity_ptr != entity_vector.end(); ++entity_ptr)
{
if((*entity_ptr)->GetName() == entity_name)
{
break;
}
}
// Remove the target entity
if(entity_ptr != entity_vector.end())
{
delete *entity_ptr;
entity_vector.erase(entity_ptr);
}
}
I have already checked that it works under C++ without exposed to python. In python, the parts of AddEntity and finding target entity are success, but it crashes at the delete * instruction of RemoveEntity (I check these by adding log instructions after each line of the codes). This is my test code in python:
import my_lib
test_a = my_lib.A("Test A")
test_e = my_lib.Entity("Test Entity")
test_a.AddEntity(test_e)
test_a.RemoveEntity("Test Entity")
I think maybe I do the exposing of std::vector<Entity *> Incorrectly, but how can I correct this?
test_e = my_lib.Entity("Test Entity")
creates a Python object that owns the C++ Entity. It manages the lifetime.
test_a.AddEntity(test_e)
here we pass the C++ object wrapped by test_e to the C++ object wrapped by test_a. The C++ object is stored within the vector of the A.
test_a.RemoveEntity("Test Entity")
this deletes the Entity you added above. However, the test_e class *still thinks it owns the Entity, as there is no way for it to be told you passed ownership away. But now it owns an invalid pointer.
The crash happens at the delete because C++ isn't managing that memory -- python is. It doesn't use the C++ heap.
You need to have a firmer design for ownership and lifetime management in the C++ code. Never delete what you did not new, and the C++ code in the python case never newed that Entity.
im trying to implement array in my c++ simulation (this coding provided is to have 2 values in one network packet field)
this is my variable declaration (in header)
Ptr<Name> m_names [2];
this is my function declaration (in header)
void SetName (unsigned which, Ptr<Name> name);
void SetName (unsigned which, const Name &name);
in source file
void Interest::SetName (unsigned which, Ptr<Name> name)
{
if (which < 2)
{
m_names[which] = name;
}
}
void Interest::SetName (unsigned which, const Name &name)
{
if (which < 2)
{
m_names[which] = Create<Name> (name);
}
}
The way I call this is like this (in my main file):
interest->SetName (0, nameWithSequence);
interest->SetName (1, nameWithNextSequence);
as a result it give error like this
src/ndnSIM/bindings/ns3module.cc: In function ‘PyObject* _wrap_PyNs3NdnData_SetName__0(PyNs3NdnData*, PyObject*, PyObject*, PyObject**)’:
src/ndnSIM/bindings/ns3module.cc:8418:62: error: no matching function for call to ‘ns3::ndn::Data::SetName(ns3::Ptr<ns3::ndn::Name>)’
src/ndnSIM/bindings/ns3module.cc:8418:62: note: candidates are:
./ns3/ndn-data.h:60:3: note: void ns3::ndn::Data::SetName(unsigned int, ns3::Ptr<ns3::ndn::Name>)
./ns3/ndn-data.h:60:3: note: candidate expects 2 arguments, 1 provided
the question is what is the correct way should i declare the correct statement. Appreciate any kind of help
EDIT
I have found some of the pyhton definition to bind my c++ code (SetName)
def __setattr__(self, name, value):
if name == "_interest":
return object.__setattr__ (self, name, value)
elif name == "name":
if value is None:
return self._interest.SetName (ns.ndnSIM.ndn.Name ())
elif isinstance (value, Name):
return self._interest.SetName (value._name)
elif isinstance (value, ns.ndnSIM.ndn.Name):
return self._interest.SetName (value)
elif isinstance (value, str):
return self._interest.SetName (ns.ndnSIM.ndn.Name (value))
else:
raise ValueError ("Invalid name parameter")
How and what is the correct way to solve this. Thanks
Okay.. let me just give you an example.
I assume Ptr acts something like std::shared_pointer, and Name like a std::string (at least for an example purposes).
First of all, i'd recommend using std::array instead of a c-style array. But that's not really neccesary.
Second. If you plan m_names to be an array of a fidex size, you can throw an exception when accessing to a non-existent member.
Here is some code.
Header (test.h):
#include <string>
#include <memory>
#include <array>
typedef std::shared_ptr<std::string> pString;
struct A{
protected:
static constexpr size_t array_size = 2;
std::array<pString, array_size> m_names;
static void CheckRange(size_t);
public:
void SetName(size_t id, const std::string& name);
void SetName(size_t id, const pString& name);
const std::string& GetName(size_t id) const;
const pString& GetNamePtr(size_t id) const;
};
Source:
#include "test.h"
#include <exception>
void A::SetName(size_t id, const pString &name){
CheckRange(id);
m_names[id] = name;
}
void A::SetName(size_t id, const std::string& name){
CheckRange(id);
m_names[id] = std::make_shared<std::string>(name);
}
const std::string& A::GetName(size_t id) const{
CheckRange(id);
if (!m_names[id])
throw std::logic_error("Pointer is not initialized");
return *(m_names[id].get());
}
const pString& A::GetNamePtr(size_t id) const {
CheckRange(id);
return m_names[id];
}
And here is an example of a CheckRange function:
void A::CheckRange(size_t id){
if (!(id < array_size))
throw std::logic_error("ID should be < " +
std::to_string(array_size));
}
This code with some tests: http://ideone.com/rUvVhH
Hint
if (m_names[id]) checks if m_names[id] contains a valid pointer (e.g. not nullptr). I suppose Ptr has some similar functionality.
And I believe std::make_shared<T>(...) is similar to Create<T>(...).
Well, as for me, this is more or less correct way to retrieve array content :)
If something is too complicated I'll try to make it easier to understand.
You are welcomed to ask if you have any questions!
The error is at compile time in the Python binding code. So,
If the wrote the binding (wrapper) code yourself (using Python C API or boost::python), you must find where you define SetName binding, it is missing a parameter.
If binding is automatically generated by some tool (like SWIG or SIP), you have to find where the SetName binding is defined: this is what the wrapper tool will use to figure out what code to generate, the mistake is in that definition.
The Python code that you added to your post, setattr, indicates clearly that the self._interest.SetName(obj) calls are wrong: your C++ shows that SetName takes two parameters. If setattr is generated by some tool (ns3?) you have to find how it does this to give it the proper definition of SetName so that setattr will have proper call.