Creating array of swift optionals from C++

Hello, I'm trying to call C++ function that returns std::vector< std::optional< std::string > > and return it in Swift as [String?]
As std::optional cannot be directly handled by Swift, I've tried to make proxy function

If I try to make it with following signature:
swift::Array< swift::Optional< swift::String > > ConvertOptList( std::vector< std::optional< std::string > > param);

I'm getting following error:

Constraints not satisfied for class template 'Array' [with T_0_0 = swift::Optionalswift::String]
Because 'swift::isUsableInGenericContext<swift::Optionalswift::String >' evaluated to false

If I try to return std::vector of Swift optional

std::vector< swift::Optional< swift::String > >  ConvertOptList( std::vector< std::optional< std::string > > param)
{
    std::vector< swift::Optional< swift::String > > res;
    
    for( auto& obj: param )
    {
        if( obj )
        {
            auto opt = swift::Optional< swift::String >::some( swift::String( *obj ) );
            res.push_back( opt );
        }
        else
        {
            auto opt = swift::Optional< swift::String >::none();
            res.push_back( opt );
        }
    }
    return res;
}

I'm getting following errors:

Undefined symbols:
Linker command failed with exit code 1 (use -v to see invocation)

Without details on undefined symbols, but problem is with push_back.

Is there a way to correctly convert such data?

P.S. also, if I try to make function that return one optional string
swift::Optional< swift::String > GetOptString( bool fill );
it isn't imported in Swift. Is it bug or also some limitation?

Hi,

Swift/C++ interop supports std::optionals, you should be able to work with std::optionals from Swift.
Could you try to implement the conversion function in Swift?
Something like this:

// .h
typedef std::vector<std::optional<std::string>> MyCxxVector;
// .swift
func convert(_ v: MyCxxVector) -> [String?] {
  return v.map { Optional(fromCxx: $0) }
}
1 Like

I'm getting error
Cannot convert value of type 'Optional<std.__1.optional<basic_string<Int8, char_traits, allocator>>.Wrapped>' (aka 'Optional<std.__1.basic_string<Int8, char_traits, allocator>>') to closure result type 'String?'

Also in case of single optional I cannot build String from it

func GetOptString( fill: Bool ) -> String?
    {
        let res = cxx.GetOptString(fill)
        if( res.hasValue )
        {
            return String( res.value )
        }
        return nil
    }

I'm getting
No exact matches in call to initializer

Oh right, you would also need to convert Optional<...basic_string> to Optional<String>

Please try this conversion function:

func convert(_ v: MyCxxVector) -> [String?] {
    return v.map {
        if let o = Optional(fromCxx: $0) {
            return String(o)
        } else {
            return nil
        }
    }
}

Yes, it works like that. Thanks

@egor.zhdan How do you go the other way around? I need to initialize a std::optional argument in a C++ function from Swift? Specifically, a std::optional<std::filesystem::path>. I know how to init the path, but can't figure out how to wrap it correctly in Swift to pass it off to C++.