Need help compiling a JNI program on VS2022 - visual-studio

Am trying to compile a very elementary JNI program. The Java code is :
public class Helloworld {
public static void main(String[] args) {
System.out.println("Hello World !!");
new Helloworld().sayHello();
}
private native void sayHello();
}
The next step I took was to compile it outside the IDE (Intellij IDEA), using the command line option of javac -h ...
This produces a machine generated Helloworld.h file.
Next I wrote an equally elementary C program, code here :
#include "Helloworld.h"
JNIEXPORT void JNICALL Java_Helloworld_sayHello(JNIEnv *, jobject){
printf("\nSay something... Anything...");
}
This I tried compiling using VS 2022 dll option, first with pre-compiled headers, and then without the precompiled headers, but no luck. A vast array of errors manifested themselves. It would have been ideal to have this compiled within the VS environment, since the actual code that is planned to be called from Java contains a mix of C and AVX512 assembly, so having the IDE support would have been ideal (especially during debugging). Any advice on how to compile this to a .dll library that would be acceptable to Java would be very welcome.
Then I moved out of VS 2022, and used the following command line compilation string :
cl /c /I "C:\\Program Files\\Microsoft\\jdk\\include\\" /I "C:\\Program Files\\Microsoft\\jdk\\include\\win32\\" .\Helloworld.c
This gives the following very persistent error :
Microsoft (R) C/C++ Optimizing Compiler Version 19.31.31106.2 for x64
Copyright (C) Microsoft Corporation. All rights reserved.
Helloworld.c
.\Helloworld.c(3): error C2055: expected formal parameter list, not a type list
I tried changing the formal parameter from jobject to jobject j, but that did not succeed (obviously, since the definition in the jni.h states jobject is a typedef for a _jobject *).
I tried to undefine the __cplusplus by using the cl command option /U __cplusplus but no joy. Not sure what to try next.
Any advice is welcome.
Thanks

From Microsoft's documentation:
expected formal parameter list, not a type list
A function definition contains a parameter type list instead of a formal parameter list. ANSI C requires formal parameters to be named unless they are void or an ellipsis (...).
void func(int, char) {} // C2055
void func (int i, char c) {} // OK
Your function definition is the one in your .c file, i.e.
JNIEXPORT void JNICALL Java_Helloworld_sayHello(JNIEnv *, jobject) {
...
}
which you need to change into something like
JNIEXPORT void JNICALL Java_Helloworld_sayHello(JNIEnv *env, jobject thiz) {
...
}
Any corresponding function declaration (e.g. in a .h file) should work with or without parameter names.

Related

Does MSP430 GCC support newer C++ standards? (like 11, 14, 17)

I'm writing some code that would greatly benefit from the concise syntax of lambdas, which were introduced with C++ 11. Is this supported by the compiler?
How do I specify the compiler flags when compiling using Energia or embedXcode?
As of February 2018, up to C++14 is supported with some limitations:
http://processors.wiki.ti.com/index.php/C%2B%2B_Support_in_TI_Compilers
There isn't much about this topic on the TI site, or, at least, I don't know enough C++ to give you a detailed and precise response.
The implementation of the embedded ABI is described in this document that is mainly a derivation of the Itanium C++ ABI. It explains nothing about the implementation of lambdas nor the auto, keyword (or probably I'm not able to derive this information from the documentation).
Thus I decided to directly test in Energia. Apparently the g++ version is 4.6.3, thus it should support both.
And in fact (from a compilation point of view, I don't have my MSP here to test the code) it can compile something like:
// In template.hpp
#ifndef TEMPLATE_HPP_
#define TEMPLATE_HPP_
template<class T>
T func(T a) {
auto c = [&](int n) { return n + a; };
return c(0);
}
#endif /* TEMPLATE_HPP_ */
// in the sketch main
#include "template.hpp"
void setup() { int b = func<int>(0); }
void loop() { }
(the template works only if in an header, in the main sketch raises an error). To compile this sketch I had to modify one internal file of the editor. The maximum supported standard seems to be -std=c++0x, and the compilation flags are in the file:
$ENERGIA_ROOT/hardware/energia/msp430/platform.txt
in my setup the root is in /opt/energia. Inside that file I modified line 32 (compiler.cpp.flags) and added the option. Notice that -std=c++11 is not supported (raises an error).
compiler.cpp.flags=-std=c++0x -c -g -O2 {compiler.mlarge_flag} {compiler.warning_flags} -fno-exceptions -ffunction-sections -fdata-sections -fno-threadsafe-statics -MMD
Unfortunately I have zero experience with embedXcode :\
Mimic std::function
std::function is not provided, thus you have to write some sort of class that mimics it. Something like:
// callback.hpp
#ifndef CALLBACK_HPP_
#define CALLBACK_HPP_
template <class RET, class ARG>
class Callback {
RET (*_f)(ARG);
public:
Callback() : _f(0) { };
Callback(RET (*f)(ARG)) : _f(f) { };
bool is_set() const { return (_f) ? true : false; }
RET operator()(ARG a) const { return is_set() ? _f(a) : 0; }
};
#endif /* CALLBACK_HPP_ */
// sketch
#include "callback.hpp"
// | !! empty capture!
void setup() { // V
auto clb = Callback<int, char>([](char c) { return (int)c; });
if (clb.is_set())
auto b = clb('a');
}
void loop() {}
may do the work, and it uses a simple trick:
The closure type for a lambda-expression with no lambda-capture has a public non-virtual non-explicit const conversion function to pointer to function having the same parameter and return types as the closure type’s function call operator. [C++11 standard 5.1.2]
As soon as you leave the capture empty, you are assured to have a "conversion" to a function pointer, thus you can store it without issues. The code I have written:
requires a first template RET that is the returned type
requires a second template ARG that is one argument for the callback. In the majority of the case you may consider to use void* as common argument (cast a struct pointer in a void pointer and use it as argument, to counter-cast in the function, the operation costs nothing)
implements two constructors: the empty constructor initialize the function pointer to NULL, while the second directly assigns the callback. Notice that the copy constructor is missing, you need to implement it.
implements a method to call the function (overloading the operator ()) and to check if the callback actually exists.
Again: this stuff compiles with no warnings, but I don't know if it works on the MSP430, since I cannot test it (it works on a common amd64 linux system).

Clang compilation error: "variable 'array' with variably modified type cannot be captured in lambda expression

In the code listed below, "LambdaTest" fails with the following error on Clang only:
shared/LambdaTest.cpp:8:31: error: variable 'array' with variably
modified type cannot be captured in a lambda expression
auto myLambdaFunction = [&array]()
^
shared/LambdaTest.cpp:7:9: note: 'array' declared here
int array[length];
The function "LambdaTest2" which passes the array as a parameter instead of capturing compiles fine on G++/Clang.
// Compiles with G++ but fails in Clang
void LambdaTest(int length)
{
int array[length];
auto myLambdaFunction = [&array]()
{
array[0] = 2;
};
myLambdaFunction();
}
// Compiles OK with G++ and Clang
void LambdaTest2(int length)
{
int array[length];
auto myLambdaFunction = [](int* myarray)
{
myarray[0] = 2;
};
myLambdaFunction(array);
}
Two questions:
What does the compiler error message "variable 'array' with variably modified type cannot be captured in a lambda expression" mean?
Why does LambdaTest fail to compile on Clang and not G++?
Thanks in advance.
COMPILER VERSIONS:
*G++ version 4.6.3
*clang version 3.5.0.210790
int array[length]; is not allowed in Standard C++. The dimension of an array must be known at compile-time.
What you are seeing is typical for non-standard features: they conflict with standard features at some point. The reason this isn't standard is because nobody has been able to make a satisfactory proposal that resolves those conflicts. Instead, each compiler has done their own thing.
You will have to either stop using the non-standard feature, or live with what a compiler happens to do with it.
VLA (Variable-length array) is not officially supported in C++.
You can instead use std::vector like so:
void LambdaTest(int length)
{
std::vector<int> array(length);
auto myLambdaFunction = [&array]()
{
array[0] = 2;
};
myLambdaFunction();
}
Thanks to both answers above for pointing out that VLAs are non-standard. Now I know what to search for.
Here are more are related links to the subject.
Why aren't variable-length arrays part of the C++ standard?
Why no VLAS in C++

c++ unmanaged dll used by dllimport in C# produces entry point not found error

I know this question has been asked here many times before. I've read all those questions that I can find along with information outside of stackoverflow. So far I haven't found an answer i can figure out that will solve the specific problem that I'm having.
here is the code for the unmanaged c++ dll header file.
namespace MyWin32DLL
{
class MyWin32ClassOne
{
public:
static __declspec(dllexport) int Getvar();
};
}
Here is the code for the c++ dll cpp file
#include "MyWin32ClassOne.h"
namespace MyWin32DLL
{
int MyWin32ClassOne::Getvar()
{
return 123;
}
}
This code i've put together from various sources so it may not be right at all. I'm not very experienced with c++ or dll's.
Here is the code from my silly little c# winforms prog that I attempt to access the dll with. (edited to correct type mismatch as pointed out by tolanj in the comments)
namespace TestDll
{
public partial class Form1 : Form
{
[DllImport("MyWin32CppDll.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int Getvar();
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
string response = Getvar().ToString();
MessageBox.Show(response, "title", MessageBoxButtons.OK);
}
}
}
Now, at this point i understand that i'm probalby getting the "no entry point" error because of how the c++ compiler mangles the names of methods and functions.
From what i've read there are two things I can do to solve the problem.
Thing 1
Add extern "c" before my declaration so the name doesn't get mangled by the compiler.
namespace MyWin32DLL
{
class MyWin32ClassOne
{
public:
extern "C" static __declspec(dllexport) int Getvar();
};
}
When i try this I get an error from Visual Studio stating that "linkage specification isn't allowed".
Ok, so I try thing 2 which is to use dumpbin to find the mangled name of my function and use the mangled name as the entry point in the dllimport call.
So i run dumpbin /symbols on my dll and i get no function name, mangled or otherwise.
Dump of file mywin32cppdll.dll
File Type: DLL
Summary
1000 .data
1000 .idata
2000 .rdata
1000 .reloc
1000 .rsrc
4000 .text
10000 .textbss
Next i try dumpbin /exports
Dump of file mywin32cppdll.dll
File Type: DLL
Section contains the following exports for MyWin32CppDll.dll
00000000 characteristics
554CF7D4 time date stamp Fri May 08 13:52:20 2015
0.00 version
1 ordinal base
1 number of functions
1 number of names
ordinal hint RVA name
1 0 00011005 ?Getvar#MyWin32ClassOne#MyWin32DLL##SAHXZ = #ILT+0(?Getvar#MyWin32ClassOne#MyWin32DLL##SAHXZ)
Summary
1000 .data
1000 .idata
2000 .rdata
1000 .reloc
1000 .rsrc
4000 .text
10000 .textbss
Looking at that I don't see a mangled or decorated name to use. But as a larth i use "Getvar#MyWin32ClassOne#MyWin32DLL##SAHXZ" as my entry point and still get the same error in my c# program.
Obviously I've missed something. How do I access the dll function from my c# program?
The name has been mangled, as you have observed. You've managed to omit the ? at the beginning of the mangled name. Your import should be:
[DllImport("MyWin32CppDll.dll", CallingConvention = CallingConvention.Cdecl,
EntryPoint = "?Getvar#MyWin32ClassOne#MyWin32DLL##SAHXZ")]
public static extern int Getvar();
Do note also that your function uses the cdecl calling convention.

Steps to make a loadable DLL of some tcl methods in Visual Studio

I want to create a loadable DLL of some of my tcl methods. But I am not getting how to do this. For that I have taken a simple example of tcl api which adds two numbers and prints the sum. Now I want to create a loadable DLL for this to export this tcl functionality.
But I am not understanding how to do it in Visual Studio. I have written a C code which can call this tcl api and get the sum of two integers, but again I don't want it to do this way. I want to create a DLL file to use this tcl functionality. How can I create this DLL on Visual Studio 2010.
Below is my sample tcl program that I am using:
#!/usr/bin/env tclsh8.5
proc add_two_nos { } {
set a 10
set b 20
set c [expr { $a + $b } ]
puts " c is $c ......."
}
And here is the C code which can use this tcl functionality :
#include <tcl.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
Tcl_Interp *interp;
int code;
char *result;
Tcl_FindExecutable(argv[0]);
interp = Tcl_CreateInterp();
code = Tcl_Eval(interp, "source myscript.tcl; add_two_nos");
/* Retrieve the result... */
result = Tcl_GetString(Tcl_GetObjResult(interp));
/* Check for error! If an error, message is result. */
if (code == TCL_ERROR) {
fprintf(stderr, "ERROR in script: %s\n", result);
exit(1);
}
/* Print (normal) result if non-empty; we'll skip handling encodings for now */
if (strlen(result)) {
printf("%s\n", result);
}
/* Clean up */
Tcl_DeleteInterp(interp);
exit(0);
}
I have successfully compiled this code with the below command
gcc simple_addition_wrapper_new.c -I/usr/include/tcl8.5/ -ltcl8.5 -o simple_addition_op
The above code is working with the expected output.
What steps do I need to take to create a loadable dll for this in Visual Studio 2010?
If you look at the answers to this question: here it gives the basic outline of the process you need to go through. There are links from my answer to some Microsoft MSDN articles on creating DLLs.
To go into this in a little more detail for a C++ dll that has Tcl embedded in it.
The first step is to create a new visual studio project with the correct type, one that is going to build a dll that exports symbols. My example project is called TclEmbeddedInDll and that name appears in code in symbols such as TCLEMBEDDEDINDLL_API that are generated by Visual Studio.
The dllmain.cpp look like this:
// dllmain.cpp : Defines the entry point for the DLL application.
#include "stdafx.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
{
allocInterp() ;
break ;
}
case DLL_THREAD_ATTACH:
break ;
case DLL_THREAD_DETACH:
break ;
case DLL_PROCESS_DETACH:
{
destroyInterp() ;
break;
}
}
return TRUE;
}
The allocInterp() and destroyInterp() functions are defined in the TclEmbeddedInDll.h, the reason for using functions here rather than creating the Tcl_Interp directly is that it keeps the details about Tcl away from the DLL interface. If you create the interp here then you have to include tcl.h and then things get complicated when you try and use the DLL in another program.
The TclEmbeddedInDll.h and .cpp are shown next, the function fnTclEmbeddedInDll() is the one that is exported from the DLL - I'm using C linkage for this rather than C++ as it makes it easier to call the function from other languages IMHO.
// The following ifdef block is the standard way of creating macros which make exporting
// from a DLL simpler. All files within this DLL are compiled with the TCLEMBEDDEDINDLL_EXPORTS
// symbol defined on the command line. This symbol should not be defined on any project
// that uses this DLL. This way any other project whose source files include this file see
// TCLEMBEDDEDINDLL_API functions as being imported from a DLL, whereas this DLL sees symbols
// defined with this macro as being exported.
#ifdef TCLEMBEDDEDINDLL_EXPORTS
#define TCLEMBEDDEDINDLL_API __declspec(dllexport)
#else
#define TCLEMBEDDEDINDLL_API __declspec(dllimport)
#endif
extern "C" {
TCLEMBEDDEDINDLL_API void fnTclEmbeddedInDll(void);
}
void allocInterp() ;
void destroyInterp() ;
// TclEmbeddedInDll.cpp : Defines the exported functions for the DLL application.
//
#include "stdafx.h"
extern "C" {
static Tcl_Interp *interp ;
// This is an example of an exported function.
TCLEMBEDDEDINDLL_API void fnTclEmbeddedInDll(void)
{
int code;
const char *result;
code = Tcl_Eval(interp, "source simple_addition.tcl; add_two_nos");
result = Tcl_GetString(Tcl_GetObjResult(interp));
}
}
void allocInterp()
{
Tcl_FindExecutable(NULL);
interp = Tcl_CreateInterp();
}
void destroyInterp()
{
Tcl_DeleteInterp(interp);
}
The implementation of allocInterp() and destroyInterp() is very naive, no error checking is done.
Finally for the Dll the stdafx.h file ties it all together like this:
// stdafx.h : include file for standard system include files,
// or project specific include files that are used frequently, but
// are changed infrequently
//
#pragma once
#include "targetver.h"
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
// Windows Header Files:
#include <windows.h>
// TODO: reference additional headers your program requires here
#include <tcl.h>
#include "TclEmbeddedInDll.h"

Overloading conflict with vector types __m128, __m256 in GCC

I've started playing around with AVX instructions on the new Intel's Sandy Bridge processor. I'm using GCC 4.5.2, TDM-GCC 64bit build of MinGW64.
I want to overload operator<< for ostream to be able to print out the vector types __m256, __m128 etc to the console. But I'm running into an overloading conflict. The 2nd function in the following code produces an error "conflicts with previous declaration void f(__vector(8) float)":
void f(__m128 v) {
cout << 4;
}
void f(__m256 v) {
cout << 8;
}
It seems that the compiler cannot distinguish between the two types and consideres them both f(float __vector).
Is there a way around this? I haven't been able to find anything online. Any help is greatly appreciated.
I accidentally stumbled upon the answer when having a similar problem with function templates. In this case, the GCC error message actually suggested a solution:
add -fabi-version=4 compiler option.
This solves my problem, and hopefully doesn't cause any issues when linking the standard libraries.
One can read more about ABI (Application Binary Interface) and GCC at ABI Policy and Guidelines and ABI specification. ABI specifies how the functions names are mangled when the code is compiled into object files. Apparently, ABI version 3 used by GCC by default cannot distinguish between the various vector types.
I was unsatisfied with the solution of changing compiler ABI flags to solve this, so I went looking for a different solution. It seems they encountered this issue in writing the Eigen library - see this source file for details http://eigen.tuxfamily.org/dox-devel/SSE_2PacketMath_8h_source.html
My solution to this is a slightly tweaked version of theirs:
template <typename T, unsigned RegisterSize>
struct Register
{
using ValueType = T;
enum { Size = RegisterSize };
inline operator T&() { return myValue; }
inline operator const T&() const { return myValue; }
inline Register() {}
inline Register(const T & v) : myValue(v) {} // Not explicit
inline Register & operator=(const T & v)
{
myValue = v;
return *this;
}
T myValue;
};
using Register4 = Register<__m128, 4u>;
using Register8 = Register<__m256, 8u>;
// Could provide more declarations for __m128d, __m128i, etc. if needed
Using the above, you can overload on Register4, Register8, etc. or produce template functions taking Registers without running into linking issues and without changing ABI settings.

Resources