Home > Software engineering >  How can I make a superclass of a template class abstract?
How can I make a superclass of a template class abstract?

Time:09-22

I have declared the following classes in a header file (Environment.h) and I would like to make the superclass FieldAccessor abstract:

#include <jni.h>

class FieldAccessor {

    public:
        FieldAccessor(
            JNIEnv* env
        ) {
            this->jNIEnv = env;
        }

        virtual jobject getValue(jobject, jobject) = 0;

    protected:
        JNIEnv* jNIEnv;
};

template<typename Type>
class PrimitiveFieldAccessor : public FieldAccessor {
    public :
        PrimitiveFieldAccessor (
            JNIEnv* env, const char name[], const char ctorSig[],
            Type (JNIEnv::*getFieldValueFunction) (jobject, jfieldID)
        );

        jobject getValue(jobject, jobject);

    private:
        jclass type;
        jmethodID constructorId;
        Type (JNIEnv::*getFieldValueFunction) (jobject, jfieldID);
};

But I obtain the following compilation error:

F:/Shared/Workspaces/Projects/JNI/src/DriverFunctionSupplierNative.cpp: In instantiation of '_jobject* PrimitiveFieldAccessor<Type>::getValue(jobject, jobject) [with Type = long int; jobject = _jobject*]':
F:/Shared/Workspaces/Projects/JNI/src/Environment.h:73:11:   required from here
F:/Shared/Workspaces/Projects/JNI/src/DriverFunctionSupplierNative.cpp:80:2: error: must use '.*' or '->*' to call pointer-to-member function in '((PrimitiveFieldAccessor<long int>*)this)->PrimitiveFieldAccessor<long int>::getFieldValueFunction (...)', e.g. '(... ->* ((PrimitiveFieldAccessor<long int>*)this)->PrimitiveFieldAccessor<long int>::getFieldValueFunction) (...)'

This is a piece of the implementation file (DriverFunctionSupplierNative.cpp):

template<typename Type>
PrimitiveFieldAccessor<Type>::PrimitiveFieldAccessor (
    JNIEnv* env,
    const char name[],
    const char ctorSig[],
    Type (JNIEnv::*getFieldValueFunction) (jobject, jfieldID)
) : FieldAccessor(env) {
    this->jNIEnv = env;
    this->type = (jclass)jNIEnv->NewGlobalRef(env->FindClass(name));
    this->constructorId = jNIEnv->GetMethodID(this->type, "<init>", ctorSig);
    this->getFieldValueFunction = getFieldValueFunction;
}

template<typename Type>
jobject PrimitiveFieldAccessor<Type>::getValue(jobject target, jobject field) {
    jfieldID fieldId = jNIEnv->FromReflectedField(field);
    return jNIEnv->NewObject(
        this->type,
        this->constructorId,
        this->getFieldValueFunction(target, fieldId)
    );
}

CodePudding user response:

The compilation error is quite descriptive of the problem. Take the time to read them and learn what they mean:

error: must use '.*' or '->*' to call pointer-to-member function in '...', e.g. ...

Now look at how you try to call the function:

this->callTypeMethodFunction(
    ...
)

The syntax would be something like this:

(jNIEnv->*callTypeMethodFunction)(value, this->callTypeMethodId);

The same thing applies for the other pointer to the member function calls:

return jNIEnv->NewObject(
    this->type,
    this->constructorId,
    (jNIEnv->*getFieldValueFunction)(target, fieldId)
);

Now why does the compilation error only happen when you have the virtual method? This is because of how template instantiation works. You didn't post how the class will be instantiated, but looking at the symptoms is that the virtual function get instantiated early to build the vtable, but other methods (non-virtual) will only be instantiated on usage.

  • Related