5

私は、Pythonスクリプトを実行し、Objective-CとPythonの間でデータをやり取りできるObjective-Cで軽量のインターフェイスを作成しているところです。私はPyObjCとObjPを調べましたが、どちらも私が探しているものではありません(iOS <= 6.0.1向けに開発しているので、PyObjCはNSMapTableを多用するためにコンパイルされません)。

つまり、基本的には、Objective-Cで「ObjC_Class」(クリエイティブ、いいえ?)というPython型を作成し、このPythonオブジェクトをObjCオブジェクトにほぼ類似させたいと考えています。そこで、クラスの__ getattr __関数をオーバーライドして、そのクラスに相当するObjCの任意のメソッドとプロパティにアクセスできるようにすることにしました。

コードは次のとおりです。

static PyObject * ObjC_Class_getattro(ObjC_Class *self, PyObject *name)
{
    NSString *attrName = [NSString stringWithCString:PyString_AsString(name) encoding:NSUTF8StringEncoding];

    NSLog(@"Calling Object: %@", self->object);
    if([self->object respondsToSelector:NSSelectorFromString(attrName)])
    {
        methodName = attrName;
        //PyObject* (*fpFunc)(PyObject*,PyObject*) = ObjC_Class_msg_send;
        PyMethodDef methd = {[attrName UTF8String],ObjC_Class_msg_send,METH_VARARGS,[attrName UTF8String]};
        PyObject* pyName = PyString_FromString(methd.ml_name);
        PyObject* pyfoo = PyCFunction_NewEx(&methd,(PyObject*)self,pyName);
        Py_DECREF(name);

        return pyfoo;
    }
    else
    {
        return name;
    }
}

今、私が言うとき、それは完全にうまく機能します:

例1

from ObjC import ObjC_Class
new = ObjC_Class('UIView')
new.backgroundColor("asdf", 42, "some_random_string") # This does not crash

しかし、私が実行すると:

例2

from ObjC import ObjC_Class
new = ObjC_Class('UIView')
moreStuff = "some_random_string" # or "42" or [1,2,3] or anything else...
new.backgroundColor("asdf", 42, moreStuff) # !!! This does crash

それは言ってクラッシュします:

error: address doesn't contain a section that points to a section in a object file

存在しない関数を呼び出そうとするとこのエラーが発生しましたが、最初の関数が機能するのに2番目の関数が機能しない理由は想像できません。

ObjC_Class_msg_send関数の実装は次のとおりです。

static PyObject* ObjC_Class_msg_send(PyObject *self, PyObject *args)
{
    NSLog(@"Entering...");
    NSMutableArray *tmp = [[NSMutableArray alloc] init];
    for(int i=0;i<PyTuple_Size(args);i++)
    {
        [tmp addObject:Py_to_ObjC(PyTuple_GetItem(args, i))];
    }
    NSLog(@"Object: %@, Method Name: %@, args: %@", ((ObjC_Class*)self)->object, methodName, tmp);
    methodName = @"";
    return PyString_FromString("Did it actually work!?!?!");
}

Python変数を関数に渡す例を実行すると、ObjC_Class_msg_sendが呼び出される前にクラッシュします(ただし、ObjC_Class_getattroがその値を返した後)。

(ああ、ずさんなコードを許してください...私はこのプロジェクトに多くの時間を割り当てる前に、単純な概念実証を実行するように取り組んでいます)

私が言及しなかったことがあります:私のObjC_Classには、Pythonオブジェクトが表すObjective-Cオブジェクトへの参照を格納するタイプ'id'の'object'という名前の要素があります...

もう1つの補足:ほとんど静的にリンクされているPython 2.6(.3?)に対してリンクしています

更新fpFuncを静的に削除するfpFuncとして定義する ことで、クラッシュを停止させることができました...なぜそこにあるのかわかりません(愚かなコピー+貼り付け...):

static PyObject * ObjC_Class_getattro(ObjC_Class *self, PyObject *name)
{
    NSString *attrName = [NSString stringWithCString:PyString_AsString(name) encoding:NSUTF8StringEncoding];

    NSLog(@"Calling Object: %@", self->object);
    if([self->object respondsToSelector:NSSelectorFromString(attrName)])
    {
        methodName = attrName;
        //static PyObject* (*fpFunc)(PyObject*,PyObject*) = ObjC_Class_msg_send; // static now
        PyMethodDef methd = {[attrName UTF8String],ObjC_Class_msg_send,METH_VARARGS,[attrName UTF8String]};
        PyObject* pyName = PyString_FromString(methd.ml_name);
        PyObject* pyfoo = PyCFunction_NewEx(&methd,(PyObject*)self,pyName);
        Py_DECREF(name);

        return pyfoo;
    }
    else
    {
        return name;
    }
}

しかし...今、Pythonはエラーをスローしています(Python変数を引数として渡すと、つまり、上記の例2):

SystemError: Objects/methodobject.c:120: bad argument to internal function

:(私はこれまでこれを見たことがありません...

4

1 に答える 1

6

これは恥ずかしいことです...私は問題を見つけました(他の誰かが同じ問題を抱えている場合に備えて)。Python ソースからエラーを追跡したところ、エラーがここでスローされていることに気付きました。

PyObject *
PyCFunction_Call(PyObject *func, PyObject *arg, PyObject *kw)
{
    PyCFunctionObject* f = (PyCFunctionObject*)func;
    PyCFunction meth = PyCFunction_GET_FUNCTION(func);
    PyObject *self = PyCFunction_GET_SELF(func);
    Py_ssize_t size;

    switch (PyCFunction_GET_FLAGS(func) & ~(METH_CLASS | METH_STATIC | METH_COEXIST)) {
    case METH_VARARGS:
        if (kw == NULL || PyDict_Size(kw) == 0)
            return (*meth)(self, arg);
        break;
    case METH_VARARGS | METH_KEYWORDS:
    case METH_OLDARGS | METH_KEYWORDS:
        return (*(PyCFunctionWithKeywords)meth)(self, arg, kw);
    case METH_NOARGS:
        if (kw == NULL || PyDict_Size(kw) == 0) {
            size = PyTuple_GET_SIZE(arg);
            if (size == 0)
                return (*meth)(self, NULL);
            PyErr_Format(PyExc_TypeError,
                "%.200s() takes no arguments (%zd given)",
                f->m_ml->ml_name, size);
            return NULL;
        }
        break;
    case METH_O:
        if (kw == NULL || PyDict_Size(kw) == 0) {
            size = PyTuple_GET_SIZE(arg);
            if (size == 1)
                return (*meth)(self, PyTuple_GET_ITEM(arg, 0));
            PyErr_Format(PyExc_TypeError,
                "%.200s() takes exactly one argument (%zd given)",
                f->m_ml->ml_name, size);
            return NULL;
        }
        break;
    case METH_OLDARGS:
        /* the really old style */
        if (kw == NULL || PyDict_Size(kw) == 0) {
            size = PyTuple_GET_SIZE(arg);
            if (size == 1)
                arg = PyTuple_GET_ITEM(arg, 0);
            else if (size == 0)
                arg = NULL;
            return (*meth)(self, arg);
        }
        break;
    default:
        PyErr_BadInternalCall();
        return NULL;
    }
    PyErr_Format(PyExc_TypeError, "%.200s() takes no keyword arguments",
             f->m_ml->ml_name);
    return NULL;
}

だから私は、「多分私のPyMethodDefメソッドは、Pythonがそれを見るまでに範囲外(または何か)になっているかもしれない」と考えました前者がパイプラインのどこかで遅延する原因となります...)。

そこで、関数からメソッドを引き出して、それを static として宣言しました (これはおなじみのように聞こえます...)。美しく機能します。更新されたコードは次のとおりです。

static PyMethodDef methd = {"blah",ObjC_Class_msg_send,METH_VARARGS,"blech"};

static PyObject * ObjC_Class_getattro(ObjC_Class *self, PyObject *name)
{
    NSString *attrName = [NSString stringWithCString:PyString_AsString(name) encoding:NSUTF8StringEncoding];

    NSLog(@"Calling Object: %@", self->object);
    if([self->object respondsToSelector:NSSelectorFromString(attrName)])
    {
        methodName = attrName;
        PyObject* pyName = PyString_FromString(methd.ml_name);
        PyObject* pyfoo = PyCFunction_NewEx(&methd,(PyObject*)self,pyName);
        Py_DECREF(name);

        return pyfoo;
    }
    else
    {
        return name;
    }
}

では、失礼しますが、「静的」の勉強に行きます。

于 2013-01-29T18:45:11.293 に答える