How to bridge a nasty C function (with complications)

Context: I'm trying to call a complicated C function from Swift. The function is part of the unit testing of a C library (hence the motley crew of parameters). The library is only available as a dylib so I can't touch the C source and, to make matters interesting, the dylib was generated by a FORTRAN compiler and is a military product subject to export restrictions.

This is the C signature of the testing function, as presented in the library documentation:

extern void TestInterface(char cIn, char* cOut,
                          int intIn, int* intOut,
                          __int64 longIn, __int64* longOut,
                          double realIn, double* realOut,
                          char strIn[512], char strOut[512],
                          int int1DIn[3], int int1DOut[3],
                          __int64 long1DIn[3], __int64 long1DOut[3],
                          double real1DIn[3], double real1DOut[3],
                          int int2DIn[2][3], int int2DOut[2][3],
                          __int64 long2DIn[2][3], __int64 long2DOut[2][3],
                          double real2DIn[2][3], double real2DOut[2][3]);

I've placed this prototype in a bridging-header and Swift accepts it (with the exception of __int64 needing changed to __int64_t). My troubles begin! I've searched high and low for examples of how Swift sees this function .. the examples I have found have been trivial in comparison, for example: Apple Developer Documentation so:

  • Question 1 -- Is it possible to see how Swift imported the above? Seeing that would give me good hints related to how Swift should pass parameters in and out. Currently, such info is only implied by the errors generated, like:
Cannot convert value of type '[[Double]]' to expected argument type 'UnsafeMutablePointer<(Double, Double, Double)>?'

I find myself twisting around the language to supply parameters when calling this monstrosity. Even the first parameter, a simple char, has me providing, at the call site:

        CChar(("A" as Character).asciiValue!)

.. I could use Int8(65), less typing, but it obscures the intent even more.


My brain hurt when I used pointers in C decades ago, and since Swift goes Unsafe.. when pointers are involved I, frankly, am out of my depth. So:

  • Question 2 -- What would be the best resource to learn how to, for example, manipulate the Swift var real2D: [[Double]] = Array(repeating: Array(repeating: 0.0, count: 3), count: 2) into a form that I could pass into the double real2DIn[2][3] parameter of that function?

I expect once I can get the lightbulb in my head switched on, this will all seem easy and obvious; I'm not there yet.


The functions in the library that are not related to unit testing are all much simpler and, for now, as far as I've got, I have managed to tame them with @convention(c) ..

func envGetInfo(dllHandle: UnsafeMutableRawPointer) -> String {
    
    guard let envGetInfoPointer = dlsym(dllHandle, "EnvGetInfo") else {
        fatalError("dlsym failure: \(String(cString: dlerror()))")
    }

    var info128 = Array(repeating: Int8(0), count: 128)

    typealias EnvGetInfoFunction = @convention(c) (UnsafeMutablePointer<Int8>) -> Void
    let envGetInfo = unsafeBitCast(envGetInfoPointer, to: EnvGetInfoFunction.self)
    envGetInfo(&info128); info128[127] = 0
    return String(cString: info128).trimmingCharacters(in: .whitespaces)
    
}
  • This is rather ugly; would it be less so if this function was also defined in the bridging header? What criteria should considered when deciding between using the bridging-header and @convention(c)?

I've spun this topic off as a separate task while I continue learning and searching for my own answers; I suspect I'm struggling with a complicated version of something, essentially, simple. If I become knowledgable before I get responses here, I'll share my findings.

1 Like

Could you make an Obj-C shim? Would make things much more easy.

1 Like

Question 1 -- Is it possible to see how Swift imported the above?

Yes. In Xcode, enter TestInterface in a line by itself and option click on it. That brings up the Quick Help which shows what Swift thinks the function looks like. On my Mac (Xcode 14.0 in a macOS command-line tool project) I see this:

func TestInterface(
    _ cIn: CChar,
    _ cOut: UnsafeMutablePointer<CChar>!,
    _ intIn: Int32,
    _ intOut: UnsafeMutablePointer<Int32>!,
    _ longIn: __int64,
    _ longOut: UnsafeMutablePointer<__int64>!,
    _ realIn: Double,
    _ realOut: UnsafeMutablePointer<Double>!,
    _ strIn: UnsafeMutablePointer<CChar>!,
    _ strOut: UnsafeMutablePointer<CChar>!,
    _ int1DIn: UnsafeMutablePointer<Int32>!,
    _ int1DOut: UnsafeMutablePointer<Int32>!,
    _ long1DIn: UnsafeMutablePointer<__int64>!,
    _ long1DOut: UnsafeMutablePointer<__int64>!,
    _ real1DIn: UnsafeMutablePointer<Double>!,
    _ real1DOut: UnsafeMutablePointer<Double>!,
    _ int2DIn: UnsafeMutablePointer<(Int32, Int32, Int32)>!,
    _ int2DOut: UnsafeMutablePointer<(Int32, Int32, Int32)>!,
    _ long2DIn: UnsafeMutablePointer<(__int64, __int64, __int64)>!,
    _ long2DOut: UnsafeMutablePointer<(__int64, __int64, __int64)>!,
    _ real2DIn: UnsafeMutablePointer<(Double, Double, Double)>!,
    _ real2DOut: UnsafeMutablePointer<(Double, Double, Double)>!
)

Note that I’ve broken the line at the parameter boundaries to make it more readable.


Even the first parameter, a simple char, has me [jumping through hoops]

There is a UInt8(ascii:) initialiser but not one for CChar. If you need to do this a lot, add one yourself:

extension CChar {
    init(ascii v: Unicode.Scalar) {
        self = CChar(bitPattern: UInt8(ascii: v))
    }
}

Then you can do CChar(ascii: "A").


Question 2 -- What would be the best resource to learn how to … pass
[reasonable values] into the double real2DIn[2][3] parameter of that
function?

I don't think you’ll find a good resource for that. The fundamental problem here is that your C function is deeply wedded to fixed-size arrays — as befits its Fortran heritage! — and that’s one of Swift’s biggest weak points where it comes to C inter-op. So double real2DIn[2][3] is expressed in Swift UnsafeMutablePointer<(Double, Double, Double)>!:

  • The [3] component is a fixed-sized array, which Swift translates to the (Double, Double, Double) tuple.

  • The fixed size of [2] is dropped because C just passes a pointer to the base of the array. Hence the UnsafeMutablePointer.

  • Oh, and while this is clearly an input parameter, the C prototype doesn’t use const so you get UnsafeMutablePointer rather than UnsafePointer.

  • The implicit unwrap (!) is there because there are no nullability annotations, so the compiler can’t tell whether you’re allowed to pass nil or not.

Calling this from Swift isn’t actually too bad. To illustrate this I separated that one parameter out into its own function:

extern void TestInterfaceMinimal(double real2DIn[2][3]);

You can call it like so:

var real2DIn: [(Double, Double, Double)] = [
    (1.0, 2.0, 3.0),
    (4.0, 5.0, 6.0),
]
TestInterfaceMinimal(&real2DIn)

The real question is “How do you represent these fixed-size arrays to Swift?” It’s hard to answer this because the details you’ve posted don’t include any info about what these parameters mean. I suspect that they are coordinates, in which case the correct native type might be SIMD3<Double>. Given that, you can write this:

var real2DIn: [SIMD3<Double>] = [
    [1.0, 2.0, 3.0],
    [4.0, 5.0, 6.0],
]
precondition(real2DIn.count == 2)
withUnsafeMutableBytes(of: &real2DIn) { buf in
    let real2DInPtr = buf.baseAddress!.assumingMemoryBound(to: (Double, Double, Double).self)
    TestInterfaceMinimal(real2DInPtr)
}

[I believe that the inter-op guarantees for SIMD mean that SIMD3<Double> is compatible with (Double, Double, Double) and thus the assumingMemoryBound(to:) is safe, but @Andrew_Trick can say for sure. See below.]

The biggest problem with this is that you end up dealing with a pyramid of nested withUnsafeMutableBytes(of:_:) calls. Honestly, if I were in your shoes I’d probably resolve this on the C side, that is, wrap this function in C such that it exposes a nicer interface to Swift.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

9 Likes

Thanks for all this guidance .. I'm traveling today but even a quick read has given me avenues for progress.

1 Like
Here's a version that uses obj-c shim
// ----------------
//  BridgingHeader.h

#include "Shim.h"

// ----------------
//  Shim.h

#ifndef ShimH
#define ShimH

#import <Foundation/Foundation.h>
#import <inttypes.h>

NS_ASSUME_NONNULL_BEGIN

typedef struct {
    int32_t x, y, z;
} Int1D;

typedef struct {
    int64_t x, y, z;
} Long1D;

typedef struct {
    double x, y, z;
} Real1D;

typedef struct {
    Int1D a, b;
} Int2D;

typedef struct {
    Long1D a, b;
} Long2D;

typedef struct {
    Real1D a, b;
} Real2D;

void objcTestInterface(
                       char cIn, char* cOut,
                       int32_t intIn, int32_t* intOut,
                       int64_t longIn, int64_t* longOut,
                       double realIn, double* realOut,
                       NSString* strIn, NSString* __nonnull * __nonnull strOut,
                       Int1D int1DIn, Int1D* int1DOut,
                       Long1D long1DIn, Long1D* long1DOut,
                       Real1D real1DIn, Real1D* real1DOut,
                       Int2D int2DIn, Int2D* int2DOut,
                       Long2D long2DIn, Long2D* long2DOut,
                       Real2D real2DIn, Real2D* real2DOut
                       );
NS_ASSUME_NONNULL_END

#endif

// ----------------
//  Shim.m

#import "Shim.h"

typedef int64_t __int64;

extern void TestInterface(char cIn, char* cOut,
                          int intIn, int* intOut,
                          __int64 longIn, __int64* longOut,
                          double realIn, double* realOut,
                          char strIn[512], char strOut[512],
                          int int1DIn[3], int int1DOut[3],
                          __int64 long1DIn[3], __int64 long1DOut[3],
                          double real1DIn[3], double real1DOut[3],
                          int int2DIn[2][3], int int2DOut[2][3],
                          __int64 long2DIn[2][3], __int64 long2DOut[2][3],
                          double real2DIn[2][3], double real2DOut[2][3]);

void objcTestInterface(
                       char cIn, char* __nonnull cOut,
                       int32_t intIn, int32_t* __nonnull intOut,
                       int64_t longIn, int64_t* __nonnull longOut,
                       double realIn, double* __nonnull realOut,
                       NSString* __nonnull strIn, NSString* __nonnull * __nonnull strOut,
                       Int1D int1DIn, Int1D* __nonnull int1DOut,
                       Long1D long1DIn, Long1D* __nonnull long1DOut,
                       Real1D real1DIn, Real1D* __nonnull real1DOut,
                       Int2D int2DIn, Int2D* __nonnull int2DOut,
                       Long2D long2DIn, Long2D* __nonnull long2DOut,
                       Real2D real2DIn, Real2D* __nonnull real2DOut
                       )
{
    int int2DInValue[2][3] = {
        { int2DIn.a.x, int2DIn.a.y, int2DIn.a.z },
        { int2DIn.b.x, int2DIn.b.y, int2DIn.b.z }
    };
    int int2DOutValue[2][3] = {
        { int2DOut->a.x, int2DOut->a.y, int2DOut->a.z },
        { int2DOut->b.x, int2DOut->b.y, int2DOut->b.z }
    };
    int64_t long2DInValue[2][3] = {
        { long2DIn.a.x, long2DIn.a.y, long2DIn.a.z },
        { long2DIn.b.x, long2DIn.b.y, long2DIn.b.z }
    };
    int64_t long2DOutValue[2][3] = {
        { long2DOut->a.x, long2DOut->a.y, long2DOut->a.z },
        { long2DOut->b.x, long2DOut->b.y, long2DOut->b.z }
    };
    double real2DInValue[2][3] = {
        { real2DIn.a.x, real2DIn.a.y, real2DIn.a.z },
        { real2DIn.b.x, real2DIn.b.y, real2DIn.b.z }
    };
    double real2DOutValue[2][3] = {
        { real2DOut->a.x, real2DOut->a.y, real2DOut->a.z },
        { real2DOut->b.x, real2DOut->b.y, real2DOut->b.z }
    };

    char strInValue[512] = {};
    char strOutValue[512] = {};
    [strIn getCString:strInValue maxLength:sizeof(strInValue) - 1 encoding:NSASCIIStringEncoding];
    
    TestInterface(
                  cIn, cOut,
                  intIn, intOut,
                  longIn, longOut,
                  realIn, realOut,
                  strInValue, strOutValue,
                  (int*)&int1DIn, (int*)int1DOut,
                  (int64_t*)&long1DIn, (int64_t*)long1DOut,
                  (double*)&real1DIn, (double*)real1DOut,
                  int2DInValue, int2DOutValue,
                  long2DInValue, long2DOutValue,
                  real2DInValue, real2DOutValue
                  );

    int2DOut->a.x = int2DOutValue[0][0];
    int2DOut->a.y = int2DOutValue[0][1];
    int2DOut->a.z = int2DOutValue[0][2];
    int2DOut->b.x = int2DOutValue[1][0];
    int2DOut->b.y = int2DOutValue[1][1];
    int2DOut->b.z = int2DOutValue[1][2];
    
    long2DOut->a.x = long2DOutValue[0][0];
    long2DOut->a.y = long2DOutValue[0][1];
    long2DOut->a.z = long2DOutValue[0][2];
    long2DOut->b.x = long2DOutValue[1][0];
    long2DOut->b.y = long2DOutValue[1][1];
    long2DOut->b.z = long2DOutValue[1][2];

    real2DOut->a.x = real2DOutValue[0][0];
    real2DOut->a.y = real2DOutValue[0][1];
    real2DOut->a.z = real2DOutValue[0][2];
    real2DOut->b.x = real2DOutValue[1][0];
    real2DOut->b.y = real2DOutValue[1][1];
    real2DOut->b.z = real2DOutValue[1][2];
    
    *strOut = [[NSString alloc] initWithCString:strOutValue encoding:NSASCIIStringEncoding];
}

// ----------------
// test.swift

var cIn: CChar = 1
var cOut: CChar = 2
var intIn: Int32 = 3
var intOut: Int32 = 4
var longIn: Int64 = 5
var longOut: Int64 = 6
var realIn: Double = 7
var realOut: Double = 8
var strIn: String = "ABC"
var strOut: NSString = ""
var int1DIn: Int1D = .init(x: 9, y: 10, z: 11)
var int1DOut = Int1D()
var long1DIn: Long1D = .init(x: 15, y: 16, z: 17)
var long1DOut = Long1D()
var real1DIn: Real1D = .init(x: 21, y: 22, z: 23)
var real1DOut = Real1D()
var int2DIn: Int2D = .init(a: .init(x: 27, y: 28, z: 29), b: .init(x: 30, y: 31, z: 32))
var int2DOut = Int2D()
var long2DIn: Long2D = .init(a: .init(x: 39, y: 40, z: 41), b: .init(x: 42, y: 43, z: 44))
var long2DOut = Long2D()
var real2DIn: Real2D = .init(a: .init(x: 51, y: 52, z: 53), b: .init(x: 54, y: 55, z: 56))
var real2DOut = Real2D()

objcTestInterface(
    cIn, &cOut,
    intIn, &intOut,
    longIn, &longOut,
    realIn, &realOut,
    strIn, &strOut,
    int1DIn, &int1DOut,
    long1DIn, &long1DOut,
    real1DIn, &real1DOut,
    int2DIn, &int2DIn,
    long2DIn, &long2DOut,
    real2DIn, &real2DOut
)

Minimally tested and should work. Note that this code makes an assumption that C's int and long are 32 and 64 bits respectively. Also string encoding is assumed ascii (but as you mentioned Fortran - that should be fine). Worth checking what happens to a 512 byte long string (there's no room for the trailing 0 in this case).

1 Like

Do not do this! SIMD3<T> has the same size as a SIMD4<T>; there's an extra hidden padding element, so this will not have the same memory layout as (T, T, T), which you need it to have in order to interoperate with the C function in question.

8 Likes

Where is it? Indeed it is hidden as only these are shown in the SIMD3 struct:

public var x: Scalar
public var y: Scalar
public var z: Scalar

Is it possible to add hidden elements to our own structs?

1 Like

Do not do this!

Ouch. Thanks for the heads up.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

1 Like

I'm returning to this after succeeding in what I needed to do, but not being satisfied by the untidy feeling of my implementation. As a reminder, I wanted to place a Swift shim over the top of a library of a few hundred C-style functions. The above nasty is a test function to exercise all the parameter possibilities that the meaningful library functions might invoke.

[Not relevant to my question, but for a little backdrop, the verbiage necessary to call TestInterface from any particular language is used to create a set of rules that automate the generation of boilerplate wrapper code for all the 'real' functions. This has been done for ten other languages; I'm adding Swift to that collection.]

The 'ground truth' for those functions is their C headers. In order for Swift to call the C functions it interprets their function signatures from the C headers, fabricates Swift analogs (like the above) during compilation, and calls them. I have this working correctly for all the many functions (including the one above).

I don't like having to include the C headers because (a) it makes for a somewhat weird SPM Package because you can't mix languages in one target, (b) in a perfect world, or a better one, I shouldn't have to include the C headers as a compile-time crutch if I could provide the equivalent information via @convention(c) and thereby deliver a Swift-only product, and (c) people ask me why half of my "Swift" interface is composed of apparent useless C header files!

To be clear, I have accomplished my goal of shimming the libraries so I'm really only complaining about how ugly it is. But slightly more that this esthetic concern, I'm puzzled that the Swift compiler can create a way to call a function and I can't use @convention(c) to do the same. My attempts to use @convention(c) are rejected. In the horror function above, the last six parameters cause a "@convention attribute only applies to function types" error. (Those parameters are 2D arrays .. Quinn did mention earlier they are not well catered for in Swift).

But I feel that if Swift can concoct an interpretation of the function, and call into it successfully, I should able to fill a @convention function pointer with exactly the same signature and have it work too. Is it, indeed, not possible with the today's Swift, or am I missing something? Enlightenment would be gladly accepted.

1 Like

I should able to fill a @convention function pointer with exactly
the same signature and have it work too.

I’m not sure I understand your requirements here. Are you asking:

  • Can Swift natively declare a C function pointer?

  • Can you assign a Swift function to a C function pointer?

The answer to both of these is “Yes.” For the first you have this:

typealias MyCFunctionPointer = @convention(c) (_ a: CInt, _ b: CInt) -> CInt

And for the second this:

var mcfp: MyCFunctionPointer? = nil
mcfp = { a, b in
    return a + b
}

But I suspect I missed your point somehow )-:

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

1 Like

Why can Swift call the function above, but I can't mimic that, as below, without getting "@convention attribute only applies to function types":

typealias functionSignature = @convention(c) (
    CChar,
    UnsafeMutablePointer<CChar>,
    Int32,
    UnsafeMutablePointer<Int32>,
    Int64,
    UnsafeMutablePointer<Int64>,
    Double,
    UnsafeMutablePointer<Double>,
    UnsafeMutablePointer<CChar>,
    UnsafeMutablePointer<CChar>,
    UnsafeMutablePointer<Int32>,
    UnsafeMutablePointer<Int32>,
    UnsafeMutablePointer<Int64>,
    UnsafeMutablePointer<Int64>,
    UnsafeMutablePointer<Double>,
    UnsafeMutablePointer<Double>,
    UnsafeMutablePointer<(Int32, Int32, Int32)>,
    UnsafeMutablePointer<(Int32, Int32, Int32)>,
    UnsafeMutablePointer<(Int64, Int64, Int64)>,
    UnsafeMutablePointer<(Int64, Int64, Int64)>,
    UnsafeMutablePointer<(Double, Double, Double)>,
    UnsafeMutablePointer<(Double, Double, Double)>)
1 Like

Your functionSignature is a tuple type, not a function type, because you didn't specify a return type. Try this:

typealias functionSignature = @convention(c) (
    CChar,
    ... blah blah blah ...
    UnsafeMutablePointer<(Double, Double, Double)>
) -> Void
1 Like

.. thanks, Rob, but:

'(CChar, ... UnsafeMutablePointer<(Double, Double, Double)>) -> ()') 

"is not representable in Objective-C, so it cannot be used with '@convention(c)'"

I understand what the error is saying and that I'm probably running up a dead-end. I really don't want to treat this forum like a help desk; if I can't do what I want, that's fine, but I remain curious about why ..

1 Like

This is a limitation of how Swift handles C fixed-size arrays. It imported the array as a tuple but it can’t handle going the other way.

The solution is to convert this:

typealias MyFunc = @convention(c) (
    …
    UnsafeMutablePointer<(CInt, CInt, CInt)>,
    …
) -> Void

to this:

typealias MyFunc = @convention(c) (
    …
    UnsafeMutablePointer<CInt>,
    …
) -> Void

It’ll all the same as far as C is concerned and it’s actually more convenient to use the latter in Swift.

Share and Enjoy

Quinn “The Eskimo!” @ DTS @ Apple

4 Likes

Thanks Quinn, That's clear. I've seen conversations about improving the handling of C fixed-size arrays in this forum so I knew there were limitations, just not that I'd run into one! For completeness sake, in case anyone comes this way again, the final code:

public func TestInterface(_ cIn: CChar, _ cOut: UnsafeMutablePointer<CChar>,
                          _ intIn: Int32, _ intOut: UnsafeMutablePointer<Int32>,
                          _ longIn: Int64, _ longOut: UnsafeMutablePointer<Int64>,
                          _ realIn: Double, _ realOut: UnsafeMutablePointer<Double>,
                          _ strIn: UnsafeMutablePointer<CChar>, _ strOut: UnsafeMutablePointer<CChar>,
                          _ int1DIn: UnsafeMutablePointer<Int32>, _ int1DOut: UnsafeMutablePointer<Int32>,
                          _ long1DIn: UnsafeMutablePointer<Int64>, _ long1DOut: UnsafeMutablePointer<Int64>,
                          _ real1DIn: UnsafeMutablePointer<Double>, _ real1DOut: UnsafeMutablePointer<Double>,
                          _ int2DIn: UnsafeMutablePointer<(Int32, Int32, Int32)>,
                          _ int2DOut: UnsafeMutablePointer<(Int32, Int32, Int32)>,
                          _ long2DIn: UnsafeMutablePointer<(Int64, Int64, Int64)>,
                          _ long2DOut: UnsafeMutablePointer<(Int64, Int64, Int64)>,
                          _ real2DIn: UnsafeMutablePointer<(Double, Double, Double)>,
                          _ real2DOut: UnsafeMutablePointer<(Double, Double, Double)>) {

// -- rebind the arrays of tuples to arrays of tuple elements

    let _int2DIn = UnsafeMutableRawPointer(int2DIn).bindMemory(to: Int32.self, capacity: 6)
    let _int2DOut = UnsafeMutableRawPointer(int2DIn).bindMemory(to: Int32.self, capacity: 6)
    let _long2DIn = UnsafeMutableRawPointer(long2DIn).bindMemory(to: Int64.self, capacity: 6)
    let _long2DOut = UnsafeMutableRawPointer(long2DIn).bindMemory(to: Int64.self, capacity: 6)
    let _real2DIn = UnsafeMutableRawPointer(real2DIn).bindMemory(to: Double.self, capacity: 6)
    let _real2DOut = UnsafeMutableRawPointer(real2DIn).bindMemory(to: Double.self, capacity: 6)

    typealias functionSignature = @convention(c) (
        CChar, UnsafeMutablePointer<CChar>,
        Int32, UnsafeMutablePointer<Int32>,
        Int64, UnsafeMutablePointer<Int64>,
        Double, UnsafeMutablePointer<Double>,
        UnsafeMutablePointer<CChar>, UnsafeMutablePointer<CChar>,
        UnsafeMutablePointer<Int32>, UnsafeMutablePointer<Int32>,
        UnsafeMutablePointer<Int64>, UnsafeMutablePointer<Int64>,
        UnsafeMutablePointer<Double>, UnsafeMutablePointer<Double>,
        UnsafeMutablePointer<Int32>, UnsafeMutablePointer<Int32>,               // array of tuple elements
        UnsafeMutablePointer<Int64>, UnsafeMutablePointer<Int64>,               // ..
        UnsafeMutablePointer<Double>, UnsafeMutablePointer<Double>) -> Void     // ..

    let function = unsafeBitCast(getFunctionPointer(libHandle, "TestInterface"), to: functionSignature.self)

    function(cIn, cOut, intIn, intOut, longIn, longOut, realIn, realOut, strIn, strOut,
             int1DIn, int1DOut, long1DIn, long1DOut, real1DIn, real1DOut,
             _int2DIn, _int2DOut, _long2DIn, _long2DOut, _real2DIn, _real2DOut)
}

thank you ..