Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature request: respect typedef enum names #477

Open
jpsacha opened this issue May 9, 2021 · 11 comments
Open

Feature request: respect typedef enum names #477

jpsacha opened this issue May 9, 2021 · 11 comments
Labels

Comments

@jpsacha
Copy link
Member

jpsacha commented May 9, 2021

One of the patterns of using C enums is to use them in combination with typedef (for instance in Spinnaker API). Consider following enum definition.

typedef enum _ENameSpace
{
    Custom,
    Standard,
    _UndefinedNameSpace
} ENameSpace;

Using "new Info().enumerate()" a Java enum class named _ENameSpace is generated:

@Namespace("Spinnaker::GenApi") public enum _ENameSpace {
    ...
}

But the usage in the original code (C++) is without underscore: ENameSpace. The request is to provide option to make javacpp to create class without "_", using typedef name ENameSpace.

Note: following suggested in the mailing list does not work (will not create enum named ENameSpace)

new Info("Spinnaker::GenApi::ENameSpace").enumerate().javaNames("ENameSpace")

Note: a work around to make _ENameSpace work is to use:

new Info("ENameSpace")
      .valueTypes("_ENameSpace")
      .pointerTypes("@Cast(\"ENameSpace*\") @ByPtrPtr _ENameSpace")

in Java code you then need to use _ENameSpace

@saudet saudet added the bug label May 10, 2021
saudet added a commit that referenced this issue May 12, 2021
@saudet
Copy link
Member

saudet commented May 12, 2021

I've fixed that in commit 5af4255. It's going to take to the name of the typedef now, but we can change it back this way:

infoMap.put(new Info("Spinnaker::GenApi::ENameSpace").enumerate().valueTypes("_ENameSpace"))

Thanks for reporting!

@jpsacha
Copy link
Member Author

jpsacha commented May 12, 2021

Thanks for addressing this issue quickly. I see names generated correctly in the Java code.
To compile C++ code (deal with pointer use) it also needs in the preset:

infoMap.put(new Info("Spinnaker::GenApi::ENameSpace").cast().pointerTypes("Pointer"));

Does it make sense to make it part of the code generated by Parser for "typedef enum" constructs?

@saudet
Copy link
Member

saudet commented May 12, 2021

It should already be doing that. Can you give an example that fails?

@jpsacha
Copy link
Member Author

jpsacha commented May 12, 2021

Type enum in this example is called EAccessMode (similar ENameSpace referred above). The preset is called "GenICam".
The error code when compiling "jniGenICam.cpp":

X:\javacpp-presets.git\spinnaker\target\native\org\bytedeco\spinnaker\windows-x86_64\jniGenICam.cpp(1219): error C2665: 'Spinnaker::GenApi::EAccessModeClass::ToString': none of the 2 overloads could convert all the argument types
C:\Program Files\FLIR Systems\Spinnaker\include\SpinGenApi/EnumClasses.h(196): note: could be 'void Spinnaker::GenApi::EAccessModeClass::ToString(Spinnaker::GenICam::gcstring &,Spinnaker::GenApi::EAccessMode *)'
X:\javacpp-presets.git\spinnaker\target\native\org\bytedeco\spinnaker\windows-x86_64\jniGenICam.cpp(1219): note: while trying to match the argument list '(Spinnaker::GenICam::gcstring, Spinnaker::GenApi::EAccessMode)'

Example typedef enum

        typedef enum typedef enum
        {
            NI,                
            NA,              
            WO,             
            RO,              
            RW,             
            _UndefinedAccesMode, 
            _CycleDetectAccesMode 

        } EAccessMode;

Class (from Spinnaker API) for which generated JNI wrapper code has error in method ToString(GenICam::gcstring& ValueStr, EAccessMode* pValue) below

        class SPINNAKER_API EAccessModeClass
        {
          public:
            static bool FromString(const GenICam::gcstring& ValueStr, EAccessMode* pValue);

            static void ToString(GenICam::gcstring& ValueStr, EAccessMode* pValue);

            static GenICam::gcstring ToString(EAccessMode Value);
        };

In the generated JNI code (jniGenICam.cpp)) the error in this line using ToString

        Spinnaker::GenApi::EAccessModeClass::ToString(*ptr0, val1);

Complete generated JNI function code (to provide context for the line with error)

JNIEXPORT void JNICALL Java_org_bytedeco_spinnaker_GenICam_EAccessModeClass_ToString__Lorg_bytedeco_spinnaker_GenICam_gcstring_2Lorg_bytedeco_spinnaker_global_GenICam_00024EAccessMode_2(JNIEnv* env, jclass cls, jobject arg0, jobject arg1) {
    Spinnaker::GenICam::gcstring* ptr0 = arg0 == NULL ? NULL : (Spinnaker::GenICam::gcstring*)jlong_to_ptr(env->GetLongField(arg0, JavaCPP_addressFID));
    if (ptr0 == NULL) {
        env->ThrowNew(JavaCPP_getClass(env, 7), "Pointer address of argument 0 is NULL.");
        return;
    }
    jlong position0 = arg0 == NULL ? 0 : env->GetLongField(arg0, JavaCPP_positionFID);
    ptr0 += position0;
    if (arg1 == NULL) {
        env->ThrowNew(JavaCPP_getClass(env, 7), "Enum for argument 1 is NULL.");
        return;
    }
    ::Spinnaker::GenApi::EAccessMode val1 = (::Spinnaker::GenApi::EAccessMode)env->GetIntField(arg1, JavaCPP_intValueFID);
    jthrowable exc = NULL;
    try {
        Spinnaker::GenApi::EAccessModeClass::ToString(*ptr0, val1);
    } catch (...) {
        exc = JavaCPP_handleException(env, 9);
    }

    if (exc != NULL) {
        env->Throw(exc);
    }
}

@jpsacha
Copy link
Member Author

jpsacha commented May 12, 2021

Also the generated Java wrapper classes for EAccessMode and EAccessModeClass.

        @Namespace("Spinnaker::GenApi") public enum EAccessMode {
            NI(0), 
            NA(1),
            WO(2),
            RO(3),
            RW(4)
            _UndefinedAccesMode(5),
            _CycleDetectAccesMode(6);/** used internally for AccessMode cycle detection*/

            public final int value;
            private EAccessMode(int v) { this.value = v; }
            private EAccessMode(EAccessMode e) { this.value = e.value; }
            public EAccessMode intern() { for (EAccessMode e : values()) if (e.value == value) return e; return this; }
            @Override public String toString() { return intern().name(); }
        }
        @Namespace("Spinnaker::GenApi") @Properties(inherit = org.bytedeco.spinnaker.presets.GenICam.class)
public class EAccessModeClass extends Pointer {
            static { Loader.load(); }
            /** Default native constructor. */
            public EAccessModeClass() { super((Pointer)null); allocate(); }
            /** Native array allocator. Access with {@link Pointer#position(long)}. */
            public EAccessModeClass(long size) { super((Pointer)null); allocateArray(size); }
            /** Pointer cast constructor. Invokes {@link Pointer#Pointer(Pointer)}. */
            public EAccessModeClass(Pointer p) { super(p); }
            private native void allocate();
            private native void allocateArray(long size);
            @Override public EAccessModeClass position(long position) {
                return (EAccessModeClass)super.position(position);
            }
            @Override public EAccessModeClass getPointer(long i) {
                return new EAccessModeClass((Pointer)this).offsetAddress(i);
            }
        
            /**
             * Converts a string to enum value
             */
            public static native @Cast("bool") boolean FromString(@Const @ByRef gcstring ValueStr, EAccessMode pValue);

            /**
             * Converts a string to an int32_t property
             */
            public static native void ToString(@ByRef gcstring ValueStr, EAccessMode pValue);

            /**
             * Converts a string to an int32_t property
             */
            public static native @ByVal gcstring ToString(@ByVal EAccessMode Value);
        }

@saudet
Copy link
Member

saudet commented May 12, 2021

Ok, thanks, but I mean, what do the presets that you have written look like?

@jpsacha
Copy link
Member Author

jpsacha commented May 12, 2021

Below is the GenICam preset in its current shape. I can push it to my repo or a branch in javacpp-presets (it can be just added to current Spinnaker preset).

You may notice there other code to deal with typedef like typedef node_vector NodeList_t, that is new Info("Spinnaker::GenApi::NodeList_t").cast().pointerTypes("Pointer"), not clear if that is related.

If you uncomment the line at the end infoMap.put(new Info(cppName).cast().pointerTypes("Pointer")) it will generate correct JNI code, except one other issue related to new typedef enum handling (when JNI code is using enum values). I can describe that too, but I I do not want to confuse things too much.

package org.bytedeco.spinnaker.presets;

import org.bytedeco.javacpp.annotation.Platform;
import org.bytedeco.javacpp.annotation.Properties;
import org.bytedeco.javacpp.presets.javacpp;
import org.bytedeco.javacpp.tools.Info;
import org.bytedeco.javacpp.tools.InfoMap;
import org.bytedeco.javacpp.tools.InfoMapper;

/**
 * Wrapper for Point Grey Spinnaker library (the C++ API v.2).
 *
 * @author Jarek Sacha
 */
@Properties(inherit = javacpp.class,
        target = "org.bytedeco.spinnaker.GenICam",
        global = "org.bytedeco.spinnaker.global.GenICam", value = {
        @Platform(value = {"linux-x86_64", "linux-arm64", "windows"},
                include = {
                        "SpinGenApi/GCTypes.h",
                        "SpinGenApi/GCString.h",
                        "SpinGenApi/GCStringVector.h",
                        "SpinGenApi/GCUtilities.h",
                        "SpinGenApi/GCBase.h",

                        "SpinGenApi/IBoolean.h",
                        "SpinGenApi/ICommand.h",
                        "SpinGenApi/IDeviceInfo.h",
                        "SpinGenApi/IEnumEntry.h",
                        "SpinGenApi/IEnumeration.h",
                        "SpinGenApi/IFloat.h",
                        "SpinGenApi/IInteger.h",
                        "SpinGenApi/IRegister.h",
                        "SpinGenApi/ISelector.h",
                        "SpinGenApi/Reference.h",
                        "SpinGenApi/INode.h",
                        "SpinGenApi/IPort.h",
                        "SpinGenApi/IString.h",
                        "SpinGenApi/IValue.h",

                        "SpinGenApi/Autovector.h",
                        "SpinGenApi/Base.h",
                        "SpinGenApi/Container.h",
                        "SpinGenApi/EnumClasses.h",
                        "SpinGenApi/Node.h",
                        "SpinGenApi/NodeMap.h",
                        "SpinGenApi/Synch.h",
                        "SpinGenApi/Types.h",

//                      "SpinGenApi/BooleanNode.h", // INode, ValueNode, IBoolean
                        // ...
//                      "SpinGenApi/SpinnakerGenApi.h"
                },
                link = {"Spinnaker@.2"},
                includepath = "/usr/include/spinnaker"),
        @Platform(value = "linux-arm",
                link = "Spinnaker@.2"),
        @Platform(value = "windows",
                link = {"Spinnaker_v140"},
                includepath = "C:/Program Files/FLIR Systems/Spinnaker/include/"),
        @Platform(value = "windows-x86",
                linkpath = {"C:/Program Files/FLIR Systems/Spinnaker/lib/vs2015/",
                        "C:/Program Files (x86)/FLIR Systems/Spinnaker/lib/vs2015/"},
                preloadpath = {"C:/Program Files/FLIR Systems/Spinnaker/bin/vs2015/",
                        "C:/Program Files (x86)/FLIR Systems/Spinnaker/bin/vs2015/"}),
        @Platform(value = "windows-x86_64",
                linkpath = {"C:/Program Files/FLIR Systems/Spinnaker/lib64/vs2015/"},
                preloadpath = {"C:/Program Files/FLIR Systems/Spinnaker/bin64/vs2015/"})})
public class GenICam implements InfoMapper {
    public void map(InfoMap infoMap) {
        infoMap
                .put(new Info().enumerate())
                .put(new Info(
//                        "SPINNAKER_LOCAL",
                        "SPINNAKER_API",
                        // GeniCam defs
                        "GC_W64", "GC_INT64_MAX", "GC_INT64_MIN", "GC_UINT64_MAX", "GC_INT32_MAX", "GC_INT32_MIN",
                        "GC_UINT32_MAX", "GC_INT8_MAX", "GC_INT8_MIN", "GC_UINT8_MAX",
                        "GCBASE_API", "GCBASE_RTTI_CLASS_API"
                ).cppTypes().annotations().cppText(""))
                .put(new Info("GENICAM_GCC_VERSION").skip())
//                // set to parse GCUtilities.h and skip definition of GENICAM_GCC_VERSION
//                .put(new Info("defined (__GNUC__)").define(false))
                // set to parse GCUtilities.h definition of __LINE_STR__ and __LOCATION__
                .put(new Info("__LINE_STR__").cppText(""))
                .put(new Info("__LOCATION__").cppText(""))
//                // set to compile GCUtilities.h on windows
                .put(new Info("defined(UNDER_RTSS)").define(false))

                // GeniCamApi/Types.h
                .put(new Info("interface").cppTypes().annotations().cppText(""))
                .put(new Info("NO_UNDEFINEDED_REPRESENTATION").define())

                // Skip due to problems with the return type "EAccessMode", it should be with namespace "Spinnaker::GenApi::EAccessMode"
//                .put(new Info("Spinnaker::GenApi::IBase::GetAccessMode()").skip())
//                .put(new Info("Spinnaker::GenApi::IBase").skip())

                // "SpinGenApi/Container.h"
                // TODO We may just need a stub for node_vector and value_vector so other code compiles
                //   (rather than disabling individual methods in node_vector and value_vector)
                .put(new Info("Spinnaker::GenApi::node_vector::const_iterator").skip())
                .put(new Info("Spinnaker::GenApi::node_vector::erase").skip())
                .put(new Info("Spinnaker::GenApi::node_vector::begin").skip())
                .put(new Info("Spinnaker::GenApi::node_vector::end").skip())
                .put(new Info("Spinnaker::GenApi::node_vector::insert").skip())
                .put(new Info("Spinnaker::GenApi::value_vector::const_iterator").skip())
                .put(new Info("Spinnaker::GenApi::value_vector::erase").skip())
                .put(new Info("Spinnaker::GenApi::value_vector::begin").skip())
                .put(new Info("Spinnaker::GenApi::value_vector::end").skip())
                .put(new Info("Spinnaker::GenApi::value_vector::insert").skip())

                .put(new Info("Spinnaker::GenApi::IEnumerationT::const_iterator").skip())
                .put(new Info("Spinnaker::GenApi::IEnumerationT::iterator").skip())
                .put(new Info("Spinnaker::GenApi::IEnumerationT::erase").skip())
                .put(new Info("Spinnaker::GenApi::IEnumerationT::begin").skip())
                .put(new Info("Spinnaker::GenApi::IEnumerationT::end").skip())
                .put(new Info("Spinnaker::GenApi::IEnumerationT::insert").skip())

                // `typedef value_vector FeatureList_t`
                .put(new Info("Spinnaker::GenApi::FeatureList_t")
                                .valueTypes("value_vector")
//                        .pointerTypes("@Cast(\"Spinnaker::GenApi::FeatureList_t*\") @ByPtrPtr value_vector")
//                        .pointerTypes("@Cast(\"Spinnaker::GenApi::FeatureList_t&\") value_vector")
                )

                // `typedef GenICam::gcstring_vector StringList_t`
                .put(new Info("Spinnaker::GenApi::StringList_t").cast().pointerTypes("Pointer"))

                // `typedef node_vector NodeList_t`
                .put(new Info("Spinnaker::GenApi::NodeList_t").cast().pointerTypes("Pointer"))


                // "SpinGenApi/ISelector.h"
                // Ignore methods that cause JNI compilation issues: "Illegal indirection" (for FeatureList_t&)
                .put(new Info("Spinnaker::GenApi::ISelector::GetSelectedFeatures").skip())
                .put(new Info("Spinnaker::GenApi::ISelector::GetSelectingFeatures").skip())
                // "SpinGenApi/Node.h"
                .put(new Info("Spinnaker::GenApi::Node::GetSelectedFeatures").skip())
                .put(new Info("Spinnaker::GenApi::Node::GetSelectingFeatures").skip())
                .put(new Info("Spinnaker::GenApi::Node::NodeImpl").skip())

                // "SpinGenApi/INode.h"
//                .put(new Info("Spinnaker::GenApi::INode::asIReference").skip())
        ;

//        // "SpinGenApi/EnumClasses.h"
//        // `typedef enum _ENameSpace {...} ENameSpace` etc.
        final String[] typedefEnums = {
                "EAccessMode",
                "ECachingMode",
                "EDisplayNotation",
                "EEndianess",
                "EGenApiSchemaVersion",
                "EIncMode",
                "EInputDirection",
                "EInterfaceType",
                "ELinkType",
                "ENameSpace",
                "ERepresentation",
                "ESign",
                "ESlope",
                "EStandardNameSpace",
                "EVisibility",
                "EXMLValidation",
                "EYesNo"
        };
        for (String name : typedefEnums) {
            String cppName = "Spinnaker::GenApi::" + name;
//            String javaName = "_" + name;
//            infoMap.put(
//                    new Info(cppName)
//                            .enumerate()
//                            .valueTypes(javaName)
//                            .pointerTypes("@Cast(\"" + cppName + "*\") @ByPtrPtr " + javaName)
//            );
//            infoMap.put(new Info(cppName).cast().pointerTypes("Pointer"));

        }
    }
}

@jpsacha jpsacha closed this as completed May 12, 2021
@saudet
Copy link
Member

saudet commented May 12, 2021

Strange, I'm not able to reproduce that with a simpler example. There must be something wrong going on with the macros or something somewhere. In any case, you can simplify all that assuming that it works, and I'll check it out when you have something cleaner to look at. Thanks!

@jpsacha
Copy link
Member Author

jpsacha commented May 12, 2021

Since there is a workaround with new Info(cppName).cast().pointerTypes("Pointer") the above is a lesser issue. In the meantime I come across another thing that shows with the new version of javacpp.

The issue related probably to the fix (I did not see it before). The generated JNI code my not reference enum values correctly. with errors like:

'ctReadingChildren': undeclared identifier

Here is the part of the C++ header defining the typedef enum:

typedef enum _ELinkType
{
    ctParentNodes,
    ctReadingChildren,
    ctWritingChildren,
    ctInvalidatingChildren,
    ctDependingNodes,
    ctTerminalNodes   /** All indirectly connected terminal nodes*/
 } ELinkType;

Another place in C++ API referring to enum value ctReadingChildren as default value (INode.h l.143) for which the JNI code is generated with the error:

virtual void GetChildren(GenApi::NodeList_t & Children, ELinkType LinkType = ctReadingChildren) const = 0;

The JNI generated code:

ptr->GetChildren(*(Spinnaker::GenApi::NodeList_t*)ptr0, ptr1 == NULL ? Spinnaker::GenApi::ELinkType(ctReadingChildren) : *(Spinnaker::GenApi::ELinkType*)ptr1);

compiler gives error: 'ctReadingChildren': undeclared identifier

@jpsacha
Copy link
Member Author

jpsacha commented May 12, 2021

Looks that I closed this issue. It was not intentional. Wrong button.

@jpsacha jpsacha reopened this May 12, 2021
@saudet
Copy link
Member

saudet commented May 12, 2021

Hum, again I'm not able to reproduce in isolation that issue either. Could you try to figure out a minimal example that fails?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants