海外の組み込みコンパイラの注意点

組込みの開発統合環境としては、ルネサスエレクトロニクスからはCubeSuiteやTexas InstrumentsからはCode Composer Studio、Analog DevicesからはVisual DSP、IARからはEmbedded Work Bench for ARMなどなど、各マイコン・各DSPに対応した統合環境およびコンパイラがあります。この中で海外のコンパイラを使う場合にはちょっと注意が必要です。

多言語対応と言えばUnicodeですが、ソースコードはUnicode保存形式をデフォルトとするのは少ないです。そのようなコンパイラを日本語環境で扱うと使うのはMBCS、具体的にはShift-JISになります。このShift-JISは日本含めアジアの数カ国で使うだけなのでTIやアナデバなどのアメリカから見れば「何それ?」ってなるわけです。それが原因で問題になるソース例はこれ。

Test.c

double AreaOfCircle(double radius)
{
    const double pi = 3.141592;
    const double minRad = 0.1;
    double result;
    if (radius < 0)
    {
        result = minRad * minRad * pi;
        // 負値で返すべきは上位が認識可能な分解能
    }else{
        result = radius * radius * pi;
    }
    return result;
}

このコメント中、’能’はShit-JISでは0x94, 0x5cです。末尾の0x5cはASCIIでいうと ‘¥’ 。C言語的には’¥’はプリプロセサマクロで文字の連結を表します。つまりコメント末尾の文字に ‘¥’があると、その文字がShit-JISと判断できない限り、次の文字列と連結します。そのため上のソースはShift-JIS対応していないコンパイラには以下のように見えます。

Test.c(コンパイラが認識するソース)

double AreaOfCircle(double radius)
{
    const double pi = 3.141592;
    const double minRad = 0.1;
    double result;
    if (radius < 0)
    {
        result = minRad * minRad * pi;
        // 負値で返すべきは上位が認識可能な分解能    }else{
        result = radius * radius * pi;
    }
    return result;
}

よって結果がおかしくなります。そこで組み込み用に書いたソースコードの中で、上記のような問題になる文字が無いかをチェックするツールを用意してみましょう。ルートディレクトリを指定して、それ以下の対象ソースを再帰的に検索し、該当ソースファイルとその行数を出力します。

 Program.cs

namespace ShiftJISFileChecker
{
    using System;
    using System.IO;

    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length != 1)
            {
                ShowHelp(Console.Out);
                return;
            }
            Search0x5cFromFolder(Console.Out, args[0]);
        }

        private static void ShowHelp(TextWriter writer)
        {
            writer.WriteLine(@"使い方:");
            writer.WriteLine(@"コマンドラインからプログラム引数に捜索対象のディレクトリを指定します。");
            writer.WriteLine(@"指定されたフォルダを再帰的に検索し、コメント末尾に0x5Cが含まれている");
            writer.WriteLine("ファイルを出力します。");
            writer.WriteLine();
            writer.WriteLine(@"利用例:SearchJISFileChecker.exe C:\");
            writer.WriteLine(@"出力例:1件見つかりました。");
            writer.WriteLine(@"    C:\work\test.c L.20");
        }

        private static void Search0x5cFromFolder(TextWriter writer, string rootDirectory)
        {
            try
            {
                var searcher = new SearchFolderHierarchcally();
                var result = searcher.Examine(rootDirectory, Search0x5cFromFile.GetFileLines);
                if (result.Count == 0)
                {
                    writer.WriteLine("Shift-JIS問題の懸念となるソースファイルは見つかりませんでした。");
                    return;
                }
                writer.WriteLine("{0}件見つかりました。", result.Count);
                foreach (var item in result)
                {
                    writer.WriteLine(item);
                }
            }
            catch (ArgumentException e)
            {
                writer.WriteLine(e.Message);
            }
        }

    }
}

 

SearchFolderHierarchcally.cs

namespace ShiftJISFileChecker
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.IO;

    internal class SearchFolderHierarchcally
    {
        private List<string> targetExtensions = new List<string>(){
            ".c",
            ".cpp",
            ".h"
        };
        internal IList<string> Examine(string root ,Func<string, IList<string>> examineFunc){
            if (!Directory.Exists(root)){
                throw new ArgumentException("指定されたディレクトリが存在しません");
            }
            var directoryInfo = new DirectoryInfo(root);
            List<string> result = new List<string>();
            foreach(var file in directoryInfo.GetFiles()){
                string fileName = file.FullName;
                if( targetExtensions.Contains(Path.GetExtension(fileName),
                    StringComparer.InvariantCultureIgnoreCase)){
                    // 1ファイルを捜索 + 対象行数を結果にコピー
                    result.AddRange(examineFunc(fileName));
                }
            }
            foreach(var subDirectory in directoryInfo.GetDirectories()){
                // サブディレクトリを再帰的に検索.
                result.AddRange(Examine(subDirectory.FullName, examineFunc));
            }
            return result;
        }
    }
}

 

Search0x5cFromFile.cs

namespace ShiftJISFileChecker
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.IO;

    internal class Search0x5cFromFile
    {
        internal static string filePath;
        private const string targetChars = "―ソЫⅨ噂浬欺圭構蚕十申曾箪貼能表暴予禄兔喀媾彌拿杤歃濬畚秉綵臀藹觸軆鐔饅鷭";

        internal static IList<string> GetFileLines(string targetFilePath)
        {
            filePath = targetFilePath;
            IList<string> result = new List<string>();
            using (StreamReader reader = new StreamReader(filePath, Encoding.GetEncoding("Shift-JIS")))
            {
                ReadAndGet0x5c(reader, result);
            }
            return result;
        }

        private static void ReadAndGet0x5c(StreamReader reader, IList<string> result)
        {
            uint lineCount = 0;
            while (!reader.EndOfStream)
            {
                lineCount++;
                string line = reader.ReadLine();
                if (!line.Equals(String.Empty) &&
                    targetChars.Contains(line[line.Length - 1]))
                {
                    result.Add(filePath + " L:" + lineCount);
                }
            }
        }
    }
}

ざっつおーる。

test