Intel® Fortran Compiler
Build applications that can scale for the future with optimized code designed for Intel® Xeon® and compatible processors.
Announcements
FPGA community forums and blogs on community.intel.com are migrating to the new Altera Community and are read-only. For urgent support needs during this transition, please visit the FPGA Design Resources page or contact an Altera Authorized Distributor.

How to call CPP from Fortran

qolin
Novice
763 Views

I need to call a DLL coded in C++ from my Fortran. The methods I need to call are object-oriented, and described in .h files. Here is the first 50 or so lines from one of the .h files:

[cpp]

#ifndef BBE_PVTT_BLACK_OIL_FLUID_H
#define BBE_PVTT_BLACK_OIL_FLUID_H

#include <PVTT/API/Model/Dll.h>
#include <PVTT/API/Common/PVTTEnum.h>
#include <PVTT/API/Common/PVTTBlackOilEnum.h>
#include <PVTT/API/Common/BlackOilImpurities.h>
#include <PVTT/API/Common/BlackOilComposition.h>

#include <string>

namespace BBE { namespace PVTT { namespace Common {
  class ErrorReport;
} } }

namespace BBE { namespace PVTT { namespace Impl { namespace Command {
  class BlackOilFluidCommand;
} } } }

namespace BBE { namespace PVTT { namespace Model {

  class BBE_PVTT_API_MODEL_DECL BlackOilFluid
  {
  public:
   
    BlackOilFluid(const std::string& name="");
    BlackOilFluid(const BlackOilFluid&);
    BlackOilFluid& operator=(const BlackOilFluid&);
    ~BlackOilFluid();

    bool operator==(const BlackOilFluid&) const;
    bool operator!=(const BlackOilFluid&) const;

    std::string GetName() const;
    void SetName(const std::string& name);

    // Set properties
    void SetOilAPI(double OilAPI);  // setting oil API sets oil specific gravity and vice versa
    void SetOilSpecificGravity(double OilSpecificGravity);
    void SetGasSpecificGravity(double value, BBE::PVTT::Common::pvtEnumGasSpecGravCondition conditions= BBE::PVTT::Common::pvtCONDITION_STD);
    void SetSeparatorConditions(double separatorPressure, double separatorTemperature);
    void SetWaterSpecificGravity(double waterSpecificGravity);
    void SetWaterSalinity(double weightPercent);
    void SetImpurities(const BBE::PVTT::Common::BlackOilImpurities& impurities);
    void SetGasComposition(const BBE::PVTT::Common::BlackOilComposition& composition);
    void SetGasPpcTpc(double Ppc, double Tpc);
    void SetStockTankRelativeVolumes( double gas, double oil, double water );
    void SetDeadOilViscosityCoefficientA( double coeffA );
    void SetDeadOilViscosityCoefficientB( double coeffB );
    void SetGasSpecificHeatCapacity( double gasCp );
    void SetOilSpecificHeatCapacity( double oilCp );
    void SetWaterSpecificHeatCapacity( double waterCp );
    void SetGasThermalConductivity( double gasTc );
    void SetOilThermalConductivity( double oilTc );
    void SetWaterThermalConductivity( double waterTc );
    void SetSpecificLatentHeatOfVaporization( double specificLatentHeatOfVaporization );

[/cpp]

Ideally, I would like to wind up with Object-oriented Fortran code, where a call to one of the above methods uses standard Fortran 2003 syntax, somethig like

[fortran]

      call fluid%SetOilApi(35.0d0)

      call fluid%SetSeparatorConditions(1d6,320d0)

[/fortran]

To make this happen, I realize I need to create a Fortran INTERFACE definition to describe all the methods I need to call. But I have no real idea what this would look like ... or even if it is possible at all. Any pointers and/or advice to get me started, please?

Qolin

0 Kudos
3 Replies
eos_pengwern
Beginner
763 Views

You won't be able to call the C++ methods directly, as the Fortran ISO_C_Binding module only directly supports interfacing to C. However, you can probably write some interface functions in plain C that will enable you to create the C++ objects and then interact with them.

In your specific example, there is a further hurdle to overcome with the presence of a std::string reference in your constructor. I can't think of any way of simulating a std::string reference (or indeed any sort of reference) within Fortran, so you may at the very least need to use a char* rather than a std:;string for the object name. For a more general example, in what follows I'm going to ignore the constructor arguments altogether. Then you can define a couple of C functions along the lines of:

[cpp]
extern "C" void * createFluidObject()
{
	BlackOilFluid *fluid = new BlackOilFluid();
	return static_cast<void *>(fluid);
}


extern "C" void setOilApi(void * fluidObject, double OilApi)
{
	BlackOilFluid *fluid = static_cast<BlackOilFluid *>(fluidObject);
	fluid -> SetOilAPI(OilApi);
}

[/cpp]

with Fortran interfaces of the form:

[fortran]
interface 
	function createFluidObject() bind(c, name='createFluidObject')

		use, intrisic :: iso_c_binding

		type(c_ptr) :: createFluidObject

	end function createFluidObject
end interface

interface 
	subroutine setOilApi(fluid, OilApi) bind(c, name='setOilApi')

		use, intrisic :: iso_c_binding

		type(c_ptr), value :: fluid
		kind(real(1d0)), value :: OilApi
		

	end subroutine setOilApi
end interface

[/fortran]

...and then, when you want to instantiate your 'fluid' object and interact with it you can write your Fortran code like this:

[fortran]
fluid = createFluidObject()
call setOilApi(fluid, OilApi)

[/fortran]

...and so on.

0 Kudos
qolin
Novice
763 Views

Thanks Eos, that's exactly what I need. I have made a start on creating the vanilla C interface, and so far it seems straightforward. I'll leave the constructor fluid name til later.

 Cheers!

0 Kudos
jimdempseyatthecove
Honored Contributor III
763 Views

qolin,

Apparently you are creating the objects on the C++ side. So you could write the Fortran shell functions to pass CHARACTER*(*) for passing name (or other variable length arguments).

Jim Dempsey

0 Kudos
Reply