Note
It is highly recommended to use Microsoft Visual C++, although other compilers are also supported.
Installing Microsoft Visual C++
Microsoft Visual C++ is available as part of Microsoft Visual Studio.
Visual Studio Community is free and can be used to build production applications. We recommend Visual Studio 2022 but Visual Studio 2017, 2019, and 2022 are all acceptable. To install, download the following installer for your desired version: Microsoft Visual Studio Community Downloads
Note though that the use of the free Community edition is not permitted in "enterprise" organizations. As of May 2018, "enterprise" organizations are defined as organizations "with >250 PCs or >$1 Million US Dollars in annual revenue". See the Visual Studio Community Website and license terms for more details.
Installing MinGW-w64 with Eclipse (Not Recommended)
For testing, the Kepler release of Eclipse and the MinGW-w64 compiler were used. The standard MinGW GCC compiler is not supported at this time since it does not support 64-bit applications. MinGW-w64 was installed using the MinGW-Builds distribution.
-
Install MinGW-Build (MinGW-w64) using the mingw-builds-install.exe installer. The tutorial was tested using the following install options:
-
Version: 4.8.1
-
Architecture: x64
-
Threads: win32
-
Exception: seh
-
Build Revision: 5
-
-
Add the compiler to your PATH environment variable.
-
From the Windows Start menu, search for "Edit environment variables for your account"
-
Under "User Variables" edit the "Path" variable by appending the bin folder to the text (preceded by a semicolon). Example: "C:\Program Files\mingw-builds\x64-4.8.1-win32-seh-rev5\mingw64\bin"
-
Click OK
-
Verify the installation by going to the command line and typing:
g++ --version
-
-
Install Java Runtime Environment (JRE) or Java Development Kit (JDK) required for Eclipse. Perform an internet search for "Download Java Runtime Environment" to find the appropriate download link.
-
Install Eclipse
-
Download Eclipse Standard and Unzip the folder to complete the installation.
-
Install the C/C++ Development Tools via Eclipse
-
Open eclipse.exe.
-
Help | Install New Software
-
Work with: Kepler... download.eclipse.org...
-
Programming Languages | C/C++ Development Tools
-
-
Although the Analysis Plugin API is C++, existing Fortran code can be leveraged. There are three ways to call Fortran code from C++:
-
C++ links to a Fortran static library (.lib). See Calling Fortran from C++ - Static Library Tutorial.
-
C++ loads and calls a Fortran dynamic link library (.dll).
-
C++ shells out to a Fortran executable (.exe)
Each method has its advantage and disadvantages.
Static Library (LIB)
Pros
-
Best performance - Fortran code is physically compiled into the C++ code.
-
Easiest Integration - Very little plumbing code is required.
-
Safety - Many integration errors (i.e. wrong method name) are caught at compile-time.
-
Testing - Static libraries are very easy to unit test. Most test frameworks require that code be compiled into static libraries.
-
Installation - The static library is compiled into the calling binary. There is no extra file to install.
Cons
-
Compiler restrictions - The static library and the calling code need to be compiled using compatible compilers. Several compiler options must be in sync as well.
-
Changes to the static library require a re-compilation of the calling code.
Summary
Static libraries are ideal when you have ownership over the static library.
Dynamic Link Library (DLL)
Pros
-
High performance - However, compared to static libraries, there is an additional step of loading the library before you can call the method. However, this cost is relatively very small.
-
Compiler independent - The DLL can be compiled using any vendor.
-
Updates - The DLL can be updated in place without recompiling calling code (as long as public interfaces have not changed!).
-
Installation - The DLL can be located away from the calling code binary. You need to be careful with environment PATHs if the DLL has dependencies.
Cons
-
Safety - Integration errors are not caught until run-time. These errors are not descriptive and can manifest themselves as hard crashes.
-
Software Overhead - Loading and calling a DLL involves non-trivial plumbing code required to the load the library and then load the address of the method. This can become a major maintenance burden if there are many methods.
-
Testing - Unit testing frameworks do not work with dynamic libraries.
Summary
Dynamic libraries allow for looser integration than static libraries. This is the best option when you do not have direct control over the library. The HyperX Plugin API is implemented using DLLs so that: 1) the user can install the DLL anywhere, and 2) the user can use any compiler.
Executable (EXE)
Pros
-
Compiler independent
-
Memory - If the method requires a lot of memory (like FEA), then launching a separate process prevents memory issues.
Cons
-
Poor performance - Launching an executable requires reading and writing input and output files. Each call also needs to launch a new process in the operating system. This overhead can be upwards of 100 ms. This sort of delay is unacceptable if the method needs to be called thousands of times - many load cases, components, or sizing candidates.
-
Testing - Cannot unit test executables.
-
Safety - Error handling within an executable requires diligently writing errors to log files and having the calling code read these files.
Summary
Executables are ideal for long running processes that require a separate memory space.
This tutorial will show you how to call an Intel Fortran static library from a Microsoft Visual C++ console application using Visual Studio. Example code is provided below. See the following sections for explanations of key setup features.
Summary
-
Compile the Fortran code with C-compatible procedure names. Use the bind(c) attribute.
function EulerBuckling(E, I, L) result(Pcr) bind(c, name="EulerBuckling") -
Create a header file in C++ that matches the Fortran procedure interface. Use the extern "C" attribute. Also, remember that Fortran is pass-by-reference.
extern "C" double EulerBuckling(double* E, double* I, double* L); -
Link the C++ project to the Fortran LIB file.
Configuration Properties | Linker | Input | Additional Dependencies
Solution Setup
The sample solution consists of two projects:
-
C++ Console Application -
CppCallsFortranStaticLib -
Fortran Static Library -
StressFunctions
Source Code
StressFunctions.f90
The Fortran static library consists of a single module with an Euler buckling method.
This is standard Fortran code. The only extra attribute is the bind(c, name='[name]') statement after the subroutine declaration. This statement forces the internal naming convention to be compatible with C code.
module StressFunctions
implicit none
real(8), parameter :: PI = atan(1.0)*4.0
contains
function EulerBuckling(E, I, L) result(Pcr) bind(c, name="EulerBuckling")
real(8), intent(in) :: E, I, L
real(8) :: Pcr
Pcr = PI**2 * E*I / (L**2)
end function
end module
When troubleshooting naming issues, you can verify the function names using "dumpbin /symbols". See DUMPBIN.
dumpbin /symbols StressFunctions.lib
There are two files in the C++ application. The first is a header file, StressFunctions.h, that defines the static library interfaces.
StressFunctions.h
The extern "C" attribute forces the internal naming to be C compatible. By default Fortran passes all arguments by reference. Therefore, all parameters are passed by reference in the C declaration.
#pragma once extern "C" double EulerBuckling(double* E, double* I, double* L);
Main.cpp
The main function contains the following code. Here we are calling the EulerBuckling method defined in the Fortran static library.
#include <iostream>
#include "StressFunctions.h"
int main()
{
double E = 10.e6;
double I = 0.5;
double L = 15.0;
double Pcr = EulerBuckling(&E, &I, &L);
std::cout << "E = " << E << std::endl;
std::cout << "I = " << I << std::endl;
std::cout << "L = " << L << std::endl;
std::cout << "Pcr = " << Pcr << std::endl;
}
Fortran Static Library - Compiler Options
Output Directory
To keep the binaries organized, the output and intermediate directory are changed to the following convention. This is helpful because the C++ and Fortran have different output directory defaults.
-
Output Directory
$(SolutionDir)bin\$(PlatformName)\$(ConfigurationName) -
Intermediate Directory
$(ProjectDir)bin\$(PlatformName)\$(ConfigurationName)
C++ Console Application - Compiler Options
Output Directory
The same directory convention is applied to the C++ project. The syntax is similar to the Fortran, but it must end in a trailing slash and the macro names are slightly different.
-
Output Directory
$(SolutionDir)bin\$(Platform)\$(Configuration)\ -
Intermediate Directory
$(ProjectDir)bin\$(Platform)\$(Configuration)\
Linker
Here we point the C++ linker to the Fortran LIB file. We take advantage of the output directory structure so that we can define a single syntax for all build scenarios.
-
Configuration Properties | Linker | Input | Additional Dependencies
$(SolutionDir)bin\$(PlatformName)\$(Configuration)\StressFunctions.lib
Intel Fortran Libraries
In this application we are linking the Microsoft Visual C++ compiler to Intel Fortran. To support this we need to reference the Intel Fortran libraries in the C++ project. See Configuring Visual Studio for Mixed-Language Applications. If you use the Intel C++ compiler, this step is not needed.
-
Configuration Properties | Linker | General | Additional Library Directories
-
Win32:
$(IFORT_COMPILER15)compiler\lib\ia32 -
x64:
$(IFORT_COMPILER15)compiler\lib\intel64
-
Troubleshooting
Here are some common error messages and strategies for troubleshooting them.
error LNK2019: unresolved external symbol _EulerBuckling referenced in function _main
-
Check that the LIB file is being linked in the C++ project.
-
Check that the name of the Fortran method (with the bind(c) syntax), matches that of the C declaration.
LINK : fatal error LNK1104: cannot open file 'ifconsol.lib'
-
Check that you have the Intel lib directory referenced in the C++ project. Be sure to verify that the folder exists on your local machine and that you are pointed to the correct version for 64-bit vs 32-bit.
-
If you are intending to use Intel C++ make sure that you have the project tool set configured properly (Configuration Properties | Platform Toolset).
LINK : warning LNK4098: defaultlib 'MSVCRT' conflicts with use of other libs; use /NODEFAULTLIB:library
-
The run-time library settings in the C++ project are likely out of sync with the Fortran library.
-
If you want to use the /MT (multi-threaded) option in C++, set the equivalent parameter in Fortran. See Library Dependencies.
-
Issues have been found using /MT with Intel Fortran and Microsoft C++ with the LAPACK library. In this scenario using the Intel C++ compiler is recommended.
When developing native code, such as C++ or Fortran, it is invaluable to verify whether the proper methods and symbols are exposed, and whether the resulting binaries depend on external libraries.
This verification can be provided by tools such as the DUMPBIN utility (shipped with Visual Studio) or Dependency Walker (available as a free download).
How-To Use DUMPBIN
Verifying DLL Exports
-
From the Windows Start menu, go to Visual Studio 2017 | Developer Command Prompt for VS 2017.
This launches the command prompt.
-
Change the directory to the output folder.
cd "C:\HyperSizer Data\Plugins\Sample"
-
Launch DUMPBIN for
Hs_Sample.dll.dumpbin /exports Hs_Sample.dll
-
The output should look similar to the figure below where
Hs_Configure_Free,Hs_Configure_Get,Hs_UDefandHs_Versionare exported.
Viewing DLL Dependencies
By default compilers will create binaries that depend on external libraries. Many times this is not desired. Run the following command to verify the dependencies:
dumpbin /dependents Hs_Sample.dll
The following shows the outputs for a Microsoft Visual C++ DLL compiled in Debug mode.
-
MSVCP140D.dll- MS Visual C++ 14.0 (D)ebug -
VCRUNTIME140D.dll- Visual C Runtime 14.0 (D)ebug -
ucrtbased.dll- (U)niversal (C) (R)un(time) Base (D)ebug -
KERNEL32.dll- Win32 base API.
Because the DLL is dependent on the MS Visual C/C++ run-time, the end-user will need to install this run-time on their machine. This can be avoided by statically linking to the run-time. See Library Dependencies.
How-To Use Dependency Walker
-
Download Dependency Walker.
-
Open Dependency Walker and open
Hs_Sample.dll(File | Open). -
Click HS_SAMPLE.DLL in the explorer tree and verify the function names.
-
Dependency Walker also detects missing dependencies. For example, if
Hs_Sample.dllrequires other C++ run-time DLLs that are not present, Dependency Walker will show this error.
By default, most C++ compilers require additional DLL dependencies. This means that you will need to distribute additional DLLs to end users - typically in the form of a redistributable package. To avoid this, use static linking.
-
Microsoft and Intel C++
-
Visual Studio - Properties | C/C++ | Code Generation | Runtime Library
/MTd (multithreaded static - debug)
/MT (multithreaded static)
-
-
Intel Fortran
-
Visual Studio - Properties | Fortran | Libraries | Runtime Library
Multithreaded
Debug Multithreaded
-
-
MinGW/GCC
-
Linker
-static-libgcc -static-libstdc++
-
Dependencies can be investigated as described under DLL Exports, Dependencies and Symbols.
Data is exchanged using C++ structs. Normally, compilers will pad the fields of structure members for more efficient processing. This alignment is set at 8 bytes in the main sizing executable.
Important
It is important that the Plugin is compiled using the same alignment setting.
Note
Visual C++ and GCC: "#pragma pack". Pragma directives are used in the tutorial code.