75751


<https://fnet-tech.blogspot.com/2016/05/c-objective-c.html>  
を参考にしています。

TestObjectC.mm

[code]

    #import "TestObjectC.h"
     
    // ヘッダファイルではなく、ソースファイルで Objective-C クラスを宣言する
    @interface ClassObjC : NSObject
     
    + (void)testingMethod;
     
    @end
     
    // 通常通りソースファイルで Objective-C クラスを実装する
    @implementation ClassObjC
     
    //--------------------------------------------------
    // [Objective-C] テスト用
    //--------------------------------------------------
    + (void)testingMethod
    {
        NSLog(@"C++ から呼ばれました。");
    }
     
    @end
     
    //==================================================
    // [C++] Objective-C クラスのメソッドを呼ぶ
    //==================================================
    void TestObjectC::callObjC()
    {
        [ClassObjC testingMethod];
    }
    
[/code]

TestObjectC.h

[code]

    #ifndef TestObjectC_hpp
    #define TestObjectC_hpp
    
    //#include <stdio.h>
    #import <Foundation/Foundation.h>
    // C++ から呼ばれるクラスを宣言する
    class TestObjectC
    {
        public:
            // C++ から呼ばれる関数
            static void callObjC();
    };
    
    
    #endif /* TestObjectC_hpp */
    
[/code]

この２ファイルを追加し、この時点ではビルドが通ることを確認しました。  
このあと、C++クラスから上記を読んでみようと考え、以下を追加しました。

NSLogCall.h

[code]

    #ifndef NSLogCall_hpp
    #define NSLogCall_hpp
    
    #include <stdio.h>
    class NSLogCall {
    public:
        static NSLogCall* getInstance();
        void printLog();
    protected:
        NSLogCall();
        ~NSLogCall();
    private:
        static NSLogCall*    sInstance;
    };
    #endif /* NSLogCall_hpp */
    
[/code]

NSLogCall.cpp

[code]

    #include "NSLogCall.h"
    //#include "TestObjectC.h"※
    
    
    NSLogCall* NSLogCall::sInstance = nullptr;
    // コンストラクター
    NSLogCall::NSLogCall()
    {
    }
    // デストラクター
    NSLogCall::~NSLogCall()
    {
    }
    
    NSLogCall* NSLogCall::getInstance()
    {
        if(NSLogCall::sInstance == nullptr){
            sInstance = new NSLogCall();
        }
        return sInstance;
    }
    
    void NSLogCall::printLog() {
    //    TestObjectC::callObjC();※
    }
    
[/code]

※箇所を解除したのですが、  
Format argument not an 'NSStrting'  
のエラーが追加したファイル以外(NSObjCRuntime.h)で出ています。  
C++で使えないNSStringが影響しているとは思うのですが、C++からNSLogを使用する方法はありますでしょうか？


CC BY-SA 4.0
2021-05-09T06:41:42.313
75742
2021-05-11T18:58:22.667
44177
question
c++ c objective-c
C++でNSLogを出力させたい


Objective-C/C++の`#import`ディレクティブを含むヘッダーは、標準C/C++用のソースファイル (.c/.cpp)
からはインクルードしてはいけません（できません）。

"TestObjectC.h" からは`#import <Foundation/Foundation.h>`を除去したうえで、  
Objective-C++のソースファイル側にてインポートとインクルードを記述します。

ちなみに`NSLog()`関数自体は、Objective-C/C++のソースファイル (.m/.mm)
内であれば、CやC++の関数内でも直接呼び出せます。ただし`@autoreleasepool`ブロックを使うべきです。

[code]

    // TestObjectC.mm
    #import <Foundation/Foundation.h>
    #include "TestObjectC.h"
    
    void TestObjectC::callObjC() {
        @autoreleasepool {
            NSLog(@"C++ から呼ばれました。");
        }
    }
    
[/code]

[code]

    // xxx.m or xxx.mm
    #include "xxx.h"
    
    void printUtf8StringByNSLog(const char* utf8) {
        @autoreleasepool {
            NSString* str = [NSString stringWithUTF8String: utf8];
            NSLog(@"%@", str);
        }
    }
    
    void printUtf16StringByNSLog(const char16_t* utf16) {
        @autoreleasepool {
            NSLog(@"%S", utf16);
        }
    }
    
[/code]

[code]

    // xxx.h
    #pragma once
    
    // C++ ではなく C コードから利用する場合は <uchar.h> のインクルードが必要。
    
    extern void printUtf8StringByNSLog(const char* utf8);
    extern void printUtf16StringByNSLog(const char16_t* utf16);
    
[/code]

`NSLog()`には`const char*`を出力するための`%s`書式もありますが、UTF-8マルチバイト文字列には対応していません。

  * [String Format Specifiers](https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/Strings/Articles/formatSpecifiers.html#//apple_ref/doc/uid/TP40004265-SW1)

余談ですが、`new`に対応する`delete`を記述しないような、中途半端なシングルトンの書き方は避けるべきです。  
C++11以降では、関数内の`static`ローカル変数の初期化はスレッドセーフとなることが保証されるので、シングルトンの実装には`static`ローカル変数を使う手もあります。

  * [ブロックスコープを持つstatic変数初期化のスレッドセーフ化 - cpprefjp C++日本語リファレンス](https://cpprefjp.github.io/lang/cpp11/static_initialization_thread_safely.html)

**2021-05-12追記：**  
`<uchar.h>`は、C11の`char16_t`エイリアス型を利用する際に必要となるヘッダーで、もし`char16_t`を使わないのであれば、Cであっても特にインクルードする必要はありません。  
macOS上でXcodeを利用されているものだと仮定しますが、現状、Apple
ClangのC11対応は不完全で、UTF-16リテラルの`u`プレフィックスや、C99の`uint_least16_t`型は使えるものの、Xcode
12.4時点でも`<uchar.h>`は実装されていないようです。  
C++では`char16_t`が組み込み型であるために、利用にあたって特に何かインクルードする必要はありません。

  * <https://ja.cppreference.com/w/c/string/multibyte/char16_t>
  * <https://ja.cppreference.com/w/c/language/string_literal>
  * <https://ja.cppreference.com/w/c/types/integer>
  * <https://ja.cppreference.com/w/cpp/language/types>
  * <https://ja.cppreference.com/w/cpp/language/string_literal>

以下を事前に記述しておけば、一応Cでもコンパイルできるようになります。

[code]

    #ifndef __cplusplus
    #include <stdint.h>
    typedef uint_least16_t char16_t;
    #endif
    
[/code]


CC BY-SA 4.0
2021-05-09T18:42:37.440
75751
2021-05-11T18:58:22.667
2021-05-11T18:58:22.667
15413
15413
75742
answer