C++ wrapper class won‘t call virtual functions of an another class?

I have a c++ framework that I want to use in swift. The main header file of that framework has a client class which should be used and it has a lot of virtual functions that are already defined so it’s not a polymorphic base class. However per Swift C++ interop guide, virtual functions are not supported at the moment. So I created a wrapper c++ class with no virtual functions to interop with swift. Unfortunately when I call a method of the wrapper class and that one in turn should call a virtual function of the framework’s client class, the function call never get executed (the function should return a shared_ptr but it’s always nullptr).

So my main question: is something wrong with my code or is it not possible at the moment at all?

Thanks
Stefan

It sounds like it should work. Could you please share a code snippet / sample project that demonstrates the issue?

sure!

wrapper class is CxxTest and the framework's client class is CxxClient

CxxTest.hpp

#pragma once

#include <stdio.h>

#include <CxxClient.h>

class CxxTest {
  
public:
    CxxTest() {
        std::cout << "Hello World" << std::endl;
    }
    
    ~CxxTest() {
        std::cout << "Goodbye!" << std::endl;
    }
    
    void doTheThing();
    void parseResponse(CommandResponse* rsp);

};

CxxTest.cpp

#include "CxxTest.hpp"

void CxxTest::doTheThing() {

    ClientConfig config
    {
       "localhost:31416",
       SkipHostLaunch::No
    };
    
    auto client = std::make_unique<CxxClient>(config);

    RegisterConnectionRequest registrationReq;    
    registrationReq.companyName = "YOUR COMPANY NAME";
    registrationReq.applicationName = "YOUR APPLICATION NAME";

    auto registrationRsp = client->RegisterConnection(registrationReq);
    parseResponse(registrationRsp.get());
    
    auto sessionId = client->GetSessionId();
    std::cout << "Session ID: " << sessionId << std::endl;
    
    ...
    
}


void CxxTest::parseResponse(CommandResponse* rsp)
{
    if(rsp)
    {
        if(rsp->status.type == CommandStatusType::Completed)
        {
            std::cout << "Command completed successfully!" << std::endl;
        }
        else
        {
            std::shared_ptr<CommandError> error = rsp->error;
            std::string errStr = error ? error->errorMessage : std::string();
            
            switch(rsp->status.type)
            {
                case CommandStatusType::CompletedWithBadResponse:
                    std::cerr << "Command completed but returned an invalid response." << std::endl;
                    break;
                case CommandStatusType::Failed:
                    if(errStr.empty())
                    {
                        std::cerr << "Command failed with no error message." << std::endl;
                    }
                    else
                    {
                        std::cerr << "Command failed with error message: " << errStr << std::endl;
                    }
                    break;
                case CommandStatusType::FailedWithBadErrorResponse:
                    std::cerr << "Command failed with a bad error response." << std::endl;
                    break;
                default:
                    break;
            }
        }
    }
    else
    {
        std::cerr << "Response pointer was null" << std::endl;
    }
}

CxxClient.h

...
virtual std::shared_ptr<RegisterConnectionResponse> RegisterConnection(const RegisterConnectionRequest& request);

std::string GetSessionId() const;
...

ViewController.swift

class ViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        var cxx = CxxTest()
        cxx.doTheThing()
     
    }

}

Output

Hello World
Response pointer was null
Session ID: 
Goodbye!

By the way, the very same example code is working on a command line tool test project (c++ only).

Thanks
Stefan

Your code is fine and it should behave as expected, it sounds like it just doesn't register the connection while running in Xcode. Can you debug your RegisterConnectoin call to see why the registrationRsp is null?

Unfortunately I cannot see into the RegisterConnection call as it's in the 3rd party framework in a precompiled binary. When I step into the call, I only see this

_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX23 pointer operator->() const _NOEXCEPT {
    return __ptr_.first();
  }

coming from usr/include/c++/v1/__memory/unique_ptr.h which should be the -> operator of client. When I change client from unqiue_ptr to a stack variable, I can step into the assembler code:

CxxClient::RegisterConnection:
    0x103ac9870 <+0>:   pushq  %rbp
    0x103ac9871 <+1>:   movq   %rsp, %rbp
    0x103ac9874 <+4>:   pushq  %r15
->  0x103ac9876 <+6>:   pushq  %r14
    0x103ac9878 <+8>:   pushq  %r13
    0x103ac987a <+10>:  pushq  %r12
    0x103ac987c <+12>:  pushq  %rbx
    0x103ac987d <+13>:  subq   $0x48, %rsp
    0x103ac9881 <+17>:  movq   %rdx, %r12
    0x103ac9884 <+20>:  movq   %rsi, %r13
    0x103ac9887 <+23>:  movq   %rdi, -0x68(%rbp)
    0x103ac988b <+27>:  movl   $0xb0, %edi
    0x103ac9890 <+32>:  callq  0x1040d5408               ; symbol stub for: operator new(unsigned long)
    0x103ac9895 <+37>:  movq   %rax, %r14
    0x103ac9898 <+40>:  movq   %rax, %r15
    0x103ac989b <+43>:  xorps  %xmm0, %xmm0
    0x103ac989e <+46>:  movups %xmm0, 0x8(%rax)
    0x103ac98a2 <+50>:  leaq   0x752a5f(%rip), %rax      ; vtable for std::__1::__shared_ptr_emplace<CxxClient::RegisterConnection(RegisterConnectionRequest const&)::RegisterConnectionHandler, std::__1::allocator<CxxClient::RegisterConnection(RegisterConnectionRequest const&)::RegisterConnectionHandler>> + 16
    0x103ac98a9 <+57>:  movq   %rax, (%r14)
    0x103ac98ac <+60>:  leaq   0x74dce5(%rip), %rax      ; vtable for DefaultRequestHandler + 16
    0x103ac98b3 <+67>:  movq   %rax, 0x18(%r14)
    0x103ac98b7 <+71>:  leaq   0x20(%r14), %rbx
    0x103ac98bb <+75>:  movq   %rbx, %rdi
    0x103ac98be <+78>:  xorl   %esi, %esi
    0x103ac98c0 <+80>:  xorl   %edx, %edx
    0x103ac98c2 <+82>:  callq  0x103a106c0               ; ptsl::CommandError::CommandError(google::protobuf::Arena*, bool)
    0x103ac98c7 <+87>:  movq   %rbx, -0x60(%rbp)
    0x103ac98cb <+91>:  xorps  %xmm0, %xmm0
    0x103ac98ce <+94>:  movups %xmm0, 0x58(%r15)
    0x103ac98d3 <+99>:  movups %xmm0, 0x48(%r15)
    0x103ac98d8 <+104>: leaq   0x752a79(%rip), %rax      ; vtable for CxxClient::RegisterConnection(RegisterConnectionRequest const&)::RegisterConnectionHandler + 16
    0x103ac98df <+111>: movq   %rax, 0x18(%r15)
    0x103ac98e3 <+115>: leaq   0x68(%r14), %rdi
    0x103ac98e7 <+119>: movq   %rdi, -0x58(%rbp)
    0x103ac98eb <+123>: xorl   %esi, %esi
    0x103ac98ed <+125>: xorl   %edx, %edx
    0x103ac98ef <+127>: callq  0x103a560d0               ; ptsl::RegisterConnectionRequestBody::RegisterConnectionRequestBody(google::protobuf::Arena*, bool)
    0x103ac98f4 <+132>: leaq   0x90(%r14), %rbx
    0x103ac98fb <+139>: movq   %rbx, %rdi
    0x103ac98fe <+142>: xorl   %esi, %esi
    0x103ac9900 <+144>: xorl   %edx, %edx
    0x103ac9902 <+146>: callq  0x103a56ab0               ; ptsl::RegisterConnectionResponseBody::RegisterConnectionResponseBody(google::protobuf::Arena*, bool)
    0x103ac9907 <+151>: leaq   0x8(%r12), %rsi
    0x103ac990c <+156>: leaq   -0x40(%rbp), %rdi
    0x103ac9910 <+160>: callq  0x1040d5240               ; symbol stub for: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::basic_string(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&)
    0x103ac9915 <+165>: movq   0x70(%r15), %rax
    0x103ac9919 <+169>: movq   %rax, %rdx
    0x103ac991c <+172>: andq   $-0x4, %rdx
    0x103ac9920 <+176>: testb  $0x1, %al
    0x103ac9922 <+178>: jne    0x103ac9aa3               ; <+563>
    0x103ac9928 <+184>: leaq   0x78(%r14), %rdi
    0x103ac992c <+188>: leaq   -0x40(%rbp), %rsi
    0x103ac9930 <+192>: callq  0x103f885e0               ; google::protobuf::internal::ArenaStringPtr::Set(google::protobuf::internal::ArenaStringPtr::EmptyDefault, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&&, google::protobuf::Arena*)
    0x103ac9935 <+197>: testb  $0x1, -0x40(%rbp)
    0x103ac9939 <+201>: je     0x103ac9944               ; <+212>
    0x103ac993b <+203>: movq   -0x30(%rbp), %rdi
    0x103ac993f <+207>: callq  0x1040d53fc               ; symbol stub for: operator delete(void*)
    0x103ac9944 <+212>: addq   $0x20, %r12
    0x103ac9948 <+216>: leaq   -0x40(%rbp), %rdi
    0x103ac994c <+220>: movq   %r12, %rsi
    0x103ac994f <+223>: callq  0x1040d5240               ; symbol stub for: std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>::basic_string(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>> const&)
    0x103ac9954 <+228>: movq   0x70(%r15), %rax
    0x103ac9958 <+232>: movq   %rax, %rdx
    0x103ac995b <+235>: andq   $-0x4, %rdx
    0x103ac995f <+239>: testb  $0x1, %al
    0x103ac9961 <+241>: jne    0x103ac9aab               ; <+571>
    0x103ac9967 <+247>: leaq   0x80(%r14), %rdi
    0x103ac996e <+254>: leaq   -0x40(%rbp), %rsi
    0x103ac9972 <+258>: callq  0x103f885e0               ; google::protobuf::internal::ArenaStringPtr::Set(google::protobuf::internal::ArenaStringPtr::EmptyDefault, std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char>>&&, google::protobuf::Arena*)
    0x103ac9977 <+263>: leaq   0x18(%r14), %r12
    0x103ac997b <+267>: testb  $0x1, -0x40(%rbp)
    0x103ac997f <+271>: je     0x103ac998a               ; <+282>
    0x103ac9981 <+273>: movq   -0x30(%rbp), %rdi
    0x103ac9985 <+277>: callq  0x1040d53fc               ; symbol stub for: operator delete(void*)
    0x103ac998a <+282>: addq   $0x8, %r14
    0x103ac998e <+286>: movq   %r12, -0x50(%rbp)
    0x103ac9992 <+290>: movq   %r15, -0x48(%rbp)
    0x103ac9996 <+294>: lock   
    0x103ac9997 <+295>: addq   $0x1, 0x8(%r15)
    0x103ac999c <+300>: leaq   -0x50(%rbp), %rsi
    0x103ac99a0 <+304>: movq   %r13, %rdi
    0x103ac99a3 <+307>: movl   $0x46, %edx
    0x103ac99a8 <+312>: callq  0x103af4770               ; CxxClient::MakeStreamingRequest(std::__1::shared_ptr<DefaultRequestHandler>, CommandType)
    0x103ac99ad <+317>: movq   -0x48(%rbp), %rbx
    0x103ac99b1 <+321>: testq  %rbx, %rbx
    0x103ac99b4 <+324>: je     0x103ac99d9               ; <+361>
    0x103ac99b6 <+326>: movq   $-0x1, %rax
    0x103ac99bd <+333>: lock   
    0x103ac99be <+334>: xaddq  %rax, 0x8(%rbx)
    0x103ac99c3 <+339>: testq  %rax, %rax
    0x103ac99c6 <+342>: jne    0x103ac99d9               ; <+361>
    0x103ac99c8 <+344>: movq   (%rbx), %rax
    0x103ac99cb <+347>: movq   %rbx, %rdi
    0x103ac99ce <+350>: callq  *0x10(%rax)
    0x103ac99d1 <+353>: movq   %rbx, %rdi
    0x103ac99d4 <+356>: callq  0x1040d5300               ; symbol stub for: std::__1::__shared_weak_count::__release_weak()
    0x103ac99d9 <+361>: movq   (%r12), %rax
    0x103ac99dd <+365>: leaq   -0x40(%rbp), %rdi
    0x103ac99e1 <+369>: movq   %r12, %rsi
    0x103ac99e4 <+372>: callq  *0x78(%rax)
    0x103ac99e7 <+375>: movq   -0x40(%rbp), %rdi
    0x103ac99eb <+379>: testq  %rdi, %rdi
    0x103ac99ee <+382>: movq   -0x68(%rbp), %r12
    0x103ac99f2 <+386>: je     0x103ac9a28               ; <+440>
    0x103ac99f4 <+388>: leaq   0x74da4d(%rip), %rsi      ; typeinfo for CommandResponse
    0x103ac99fb <+395>: leaq   0x7528de(%rip), %rdx      ; typeinfo for RegisterConnectionResponse
    0x103ac9a02 <+402>: xorl   %ecx, %ecx
    0x103ac9a04 <+404>: callq  0x1040d5462               ; symbol stub for: __dynamic_cast
    0x103ac9a09 <+409>: testq  %rax, %rax
    0x103ac9a0c <+412>: je     0x103ac9a28               ; <+440>
    0x103ac9a0e <+414>: movq   -0x38(%rbp), %rcx
    0x103ac9a12 <+418>: movq   %rax, (%r12)
    0x103ac9a16 <+422>: movq   %rcx, 0x8(%r12)
    0x103ac9a1b <+427>: testq  %rcx, %rcx
    0x103ac9a1e <+430>: je     0x103ac9a30               ; <+448>
    0x103ac9a20 <+432>: lock   
    0x103ac9a21 <+433>: addq   $0x1, 0x8(%rcx)
    0x103ac9a26 <+438>: jmp    0x103ac9a30               ; <+448>
    0x103ac9a28 <+440>: xorps  %xmm0, %xmm0
    0x103ac9a2b <+443>: movups %xmm0, (%r12)
    0x103ac9a30 <+448>: movq   -0x38(%rbp), %rbx
    0x103ac9a34 <+452>: testq  %rbx, %rbx
    0x103ac9a37 <+455>: je     0x103ac9a4b               ; <+475>
    0x103ac9a39 <+457>: movq   $-0x1, %rax
    0x103ac9a40 <+464>: lock   
    0x103ac9a41 <+465>: xaddq  %rax, 0x8(%rbx)
    0x103ac9a46 <+470>: testq  %rax, %rax
    0x103ac9a49 <+473>: je     0x103ac9a7f               ; <+527>
    0x103ac9a4b <+475>: movq   $-0x1, %rax
    0x103ac9a52 <+482>: lock   
    0x103ac9a53 <+483>: xaddq  %rax, (%r14)
    0x103ac9a57 <+487>: testq  %rax, %rax
    0x103ac9a5a <+490>: jne    0x103ac9a6d               ; <+509>
    0x103ac9a5c <+492>: movq   (%r15), %rax
    0x103ac9a5f <+495>: movq   %r15, %rdi
    0x103ac9a62 <+498>: callq  *0x10(%rax)
    0x103ac9a65 <+501>: movq   %r15, %rdi
    0x103ac9a68 <+504>: callq  0x1040d5300               ; symbol stub for: std::__1::__shared_weak_count::__release_weak()
    0x103ac9a6d <+509>: movq   %r12, %rax
    0x103ac9a70 <+512>: addq   $0x48, %rsp
    0x103ac9a74 <+516>: popq   %rbx
    0x103ac9a75 <+517>: popq   %r12
    0x103ac9a77 <+519>: popq   %r13
    0x103ac9a79 <+521>: popq   %r14
    0x103ac9a7b <+523>: popq   %r15
    0x103ac9a7d <+525>: popq   %rbp
    0x103ac9a7e <+526>: retq   
    0x103ac9a7f <+527>: movq   (%rbx), %rax
    0x103ac9a82 <+530>: movq   %rbx, %rdi
    0x103ac9a85 <+533>: callq  *0x10(%rax)
    0x103ac9a88 <+536>: movq   %rbx, %rdi
    0x103ac9a8b <+539>: callq  0x1040d5300               ; symbol stub for: std::__1::__shared_weak_count::__release_weak()
    0x103ac9a90 <+544>: movq   $-0x1, %rax
    0x103ac9a97 <+551>: lock   
    0x103ac9a98 <+552>: xaddq  %rax, (%r14)
    0x103ac9a9c <+556>: testq  %rax, %rax
    0x103ac9a9f <+559>: jne    0x103ac9a6d               ; <+509>
    0x103ac9aa1 <+561>: jmp    0x103ac9a5c               ; <+492>
    0x103ac9aa3 <+563>: movq   (%rdx), %rdx
    0x103ac9aa6 <+566>: jmp    0x103ac9928               ; <+184>
    0x103ac9aab <+571>: movq   (%rdx), %rdx
    0x103ac9aae <+574>: jmp    0x103ac9967               ; <+247>
    0x103ac9ab3 <+579>: movq   %rax, %r12
    0x103ac9ab6 <+582>: jmp    0x103ac9ae7               ; <+631>
    0x103ac9ab8 <+584>: movq   %rax, %r12
    0x103ac9abb <+587>: movq   -0x48(%rbp), %rbx
    0x103ac9abf <+591>: testq  %rbx, %rbx
    0x103ac9ac2 <+594>: je     0x103ac9ae7               ; <+631>
    0x103ac9ac4 <+596>: movq   $-0x1, %rax
    0x103ac9acb <+603>: lock   
    0x103ac9acc <+604>: xaddq  %rax, 0x8(%rbx)
    0x103ac9ad1 <+609>: testq  %rax, %rax
    0x103ac9ad4 <+612>: jne    0x103ac9ae7               ; <+631>
    0x103ac9ad6 <+614>: movq   (%rbx), %rax
    0x103ac9ad9 <+617>: movq   %rbx, %rdi
    0x103ac9adc <+620>: callq  *0x10(%rax)
    0x103ac9adf <+623>: movq   %rbx, %rdi
    0x103ac9ae2 <+626>: callq  0x1040d5300               ; symbol stub for: std::__1::__shared_weak_count::__release_weak()
    0x103ac9ae7 <+631>: movq   $-0x1, %rax
    0x103ac9aee <+638>: lock   
    0x103ac9aef <+639>: xaddq  %rax, (%r14)
    0x103ac9af3 <+643>: testq  %rax, %rax
    0x103ac9af6 <+646>: jne    0x103ac9b09               ; <+665>
    0x103ac9af8 <+648>: movq   (%r15), %rax
    0x103ac9afb <+651>: movq   %r15, %rdi
    0x103ac9afe <+654>: callq  *0x10(%rax)
    0x103ac9b01 <+657>: movq   %r15, %rdi
    0x103ac9b04 <+660>: callq  0x1040d5300               ; symbol stub for: std::__1::__shared_weak_count::__release_weak()
    0x103ac9b09 <+665>: movq   %r12, %rdi
    0x103ac9b0c <+668>: callq  0x1040d50fc               ; symbol stub for: _Unwind_Resume
    0x103ac9b11 <+673>: jmp    0x103ac9b13               ; <+675>
    0x103ac9b13 <+675>: movq   %rax, %r12
    0x103ac9b16 <+678>: testb  $0x1, -0x40(%rbp)
    0x103ac9b1a <+682>: je     0x103ac9b3c               ; <+716>
    0x103ac9b1c <+684>: movq   -0x30(%rbp), %rdi
    0x103ac9b20 <+688>: callq  0x1040d53fc               ; symbol stub for: operator delete(void*)
    0x103ac9b25 <+693>: jmp    0x103ac9b3c               ; <+716>
    0x103ac9b27 <+695>: movq   %rax, %r12
    0x103ac9b2a <+698>: jmp    0x103ac9b44               ; <+724>
    0x103ac9b2c <+700>: movq   %rax, %r12
    0x103ac9b2f <+703>: jmp    0x103ac9b4d               ; <+733>
    0x103ac9b31 <+705>: movq   %rax, %r12
    0x103ac9b34 <+708>: jmp    0x103ac9bb9               ; <+841>
    0x103ac9b39 <+713>: movq   %rax, %r12
    0x103ac9b3c <+716>: movq   %rbx, %rdi
    0x103ac9b3f <+719>: callq  0x103a56c80               ; ptsl::RegisterConnectionResponseBody::~RegisterConnectionResponseBody()
    0x103ac9b44 <+724>: movq   -0x58(%rbp), %rdi
    0x103ac9b48 <+728>: callq  0x103a56300               ; ptsl::RegisterConnectionRequestBody::~RegisterConnectionRequestBody()
    0x103ac9b4d <+733>: leaq   0x74da44(%rip), %rax      ; vtable for DefaultRequestHandler + 16
    0x103ac9b54 <+740>: movq   %rax, 0x18(%r15)
    0x103ac9b58 <+744>: movq   0x60(%r15), %rbx
    0x103ac9b5c <+748>: testq  %rbx, %rbx
    0x103ac9b5f <+751>: je     0x103ac9b84               ; <+788>
    0x103ac9b61 <+753>: movq   $-0x1, %rax
    0x103ac9b68 <+760>: lock   
    0x103ac9b69 <+761>: xaddq  %rax, 0x8(%rbx)
    0x103ac9b6e <+766>: testq  %rax, %rax
    0x103ac9b71 <+769>: jne    0x103ac9b84               ; <+788>
    0x103ac9b73 <+771>: movq   (%rbx), %rax
    0x103ac9b76 <+774>: movq   %rbx, %rdi
    0x103ac9b79 <+777>: callq  *0x10(%rax)
    0x103ac9b7c <+780>: movq   %rbx, %rdi
    0x103ac9b7f <+783>: callq  0x1040d5300               ; symbol stub for: std::__1::__shared_weak_count::__release_weak()
    0x103ac9b84 <+788>: movq   0x50(%r15), %rbx
    0x103ac9b88 <+792>: testq  %rbx, %rbx
    0x103ac9b8b <+795>: je     0x103ac9bb0               ; <+832>
    0x103ac9b8d <+797>: movq   $-0x1, %rax
    0x103ac9b94 <+804>: lock   
    0x103ac9b95 <+805>: xaddq  %rax, 0x8(%rbx)
    0x103ac9b9a <+810>: testq  %rax, %rax
    0x103ac9b9d <+813>: jne    0x103ac9bb0               ; <+832>
    0x103ac9b9f <+815>: movq   (%rbx), %rax
    0x103ac9ba2 <+818>: movq   %rbx, %rdi
    0x103ac9ba5 <+821>: callq  *0x10(%rax)
    0x103ac9ba8 <+824>: movq   %rbx, %rdi
    0x103ac9bab <+827>: callq  0x1040d5300               ; symbol stub for: std::__1::__shared_weak_count::__release_weak()
    0x103ac9bb0 <+832>: movq   -0x60(%rbp), %rdi
    0x103ac9bb4 <+836>: callq  0x103a108a0               ; ptsl::CommandError::~CommandError()
    0x103ac9bb9 <+841>: movq   %r15, %rdi
    0x103ac9bbc <+844>: callq  0x1040d530c               ; symbol stub for: std::__1::__shared_weak_count::~__shared_weak_count()
    0x103ac9bc1 <+849>: movq   %r15, %rdi
    0x103ac9bc4 <+852>: callq  0x1040d53fc               ; symbol stub for: operator delete(void*)
    0x103ac9bc9 <+857>: movq   %r12, %rdi
    0x103ac9bcc <+860>: callq  0x1040d50fc               ; symbol stub for: _Unwind_Resume
    0x103ac9bd1 <+865>: nopw   %cs:(%rax,%rax)
    0x103ac9bdb <+875>: nopl   (%rax,%rax)

I compared the debugger output with the c++ command line tool's output and except the memory addresses, it's identical

Weird. The same code is working on a Swift command line tool, but not on a Swift macOS app project.
Where is the difference??? :man_shrugging:

Could it be that you are using a global variable in the command line tool and a local variable in the app?

Could it be that you are using a global variable in the command line tool and a local variable in the app?

Yes, I do, but it doesn't change the behaviour. Changing the app to

class ViewController: NSViewController {

    var cxx = CxxTest()
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        cxx.doTheThing()
        
    }
}

or even

var cxx = CxxTest()

class ViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
        cxx.doTheThing()
        
    }
}

still returns nullptr.

On the other hand, if I encapsulate my variable into a swift class as a local variable in the command line tool like this

import Foundation

print("Hello, World!")

class MyClass {

    func callWrapperFunction() {
         var cxx = CxxTest()
         cxx.doTheThing()
    }
}


let myClass = MyClass()

myClass.callWrapperFunction()

the code will run correctly and return the pointer.

Got you.

I'd try the traditional "swift -> Obj-C -> C++" in this case for a test (bridging header + .mm file with Obj-C class calling through to C++). If that works – that would be an indication of something funny in the newer "Swift -> C++" interop (would be unsurprising at this stage).

Found the error. App Sandbox was activated by default and is blocking the c++ code from executing correctly :see_no_evil:

So i can confirm, C++/Swift Interoperability is working as expected with the above example.

Sorry for the inconvenience and thanks to everybody for your help.

Stefan

1 Like