[C++] 43 ld: duplicate symbols for architecture arm64への対処

ケースバイケースですが、今回はヘッダファイルの変数にstatic修飾子を付けるとシンボルの重複が解消されて直りました。

#ifndef STDDISPLAY_H
#define STDDISPLAY_H

#include <FL/Fl_Text_Display.H>

static Fl_Text_Display *G_disp[3];
static Fl_Text_Buffer *G_buff[3];
static int G_outfd[3];
static pid_t G_pids[3];  

void start_child(int);
void data_ready(int, void*);
void close_cb(Fl_Widget*, void*);

#endif

[C++] 41 FLTK : findコマンド生成アプリ/コンソール版完成

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

findコマンド生成アプリのコンソール版が完成しました。わずか878KBの超軽量ツールです。

仕上げは標準出力のGUIへの反映になりますが、すんなり実装できればありがたいです。

ところで私のコードの書き方は、C/C++界隈ではメジャーらしいK&RスタイルではなくJavaスタイルです。情報密度と可読性を優先しているので、1行1前カッコは禁止がマイルールです。後カッコはブロックの終わりが分かりやすいため、1行表示しています。

void execute_cb(Fl_Widget*, void*){
    stringstream cmd;
    stringstream pre;
    const char* dir;

    onoff_from = from_rbtn->value(); 
    onoff_to = to_rbtn->value();
    onoff_name = name_rbtn->value();
    dir = dir_input->value();

    cmd << "cd " << string(dir) << " && " << "find `pwd` -type file ";

    if (onoff_from == 1){
        const char* from_char = from_input->value();
        string from_str = string(from_char);

        std::cout << "from_str " << from_str << endl;
        string day = from_str.substr(6);
        string month = from_str.substr(4, 2);
        string year = from_str.substr(0, 4);

        int days = GetDays(stoi(year), stoi(month), stoi(day));
        int predays = days - 1;

        std::cout << "days " << days << endl;
        std::cout << "predays " << predays << endl;
        std::cout << day << " " << month << " " << year <<endl;

        preDate = { 0, 0, 0, stoi(day) -1, stoi(month)-1, stoi(year)};
        preDate_t = std::mktime(&preDate);
        preDate2 = localtime(&preDate_t);

        int pre_y = preDate2->tm_year;
        int pre_m = preDate2->tm_mon + 1;
        int pre_d = preDate2->tm_mday;

        std::cout << "pre_y pre_m pre_d " << pre_y << " " << pre_m << " " << pre_d <<endl;

        int day2 = GetDay(pre_y, pre_m, pre_d, predays);

        std::cout << "day2 " << day2 <<endl;

        pre << pre_y;
        pre << setw(2) << setfill('0') << pre_m;
        pre << setw(2) << setfill('0') << day2;
        
        std::cout << pre.str() << endl;
        
        cmd << "-newerct '" << pre.str() << " 23:59' ";
        std::cout << cmd.str() << endl;
    }
    if (onoff_to == 1){
        const char* to_char = to_input->value();
        string to_str = string(to_char);

        cmd << "! -newerct '" << to_str << " 23:59' " ;
        std::cout << cmd.str() << endl;
    }
    if (onoff_name == 1){
        const char* name_char = name_input->value();
        string name_str = string(name_char);

        cmd << "-name '*" << name_str << "*' " ;
    }

    cmd << "! -name '*DS_Store*'" ;
    std::cout << cmd.str() << endl;
    system((cmd.str()).c_str());

    // 件数表示
    cmd << " | wc -l";
    std::cout << cmd.str() << endl;
    system((cmd.str()).c_str());
}

[C++] 40 FLTK : findコマンド生成アプリ/前日の取得 日付シリアル値

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

前回の続きです。

西暦1年1月1日からの経過日数をシリアル値とし、前回のケースであれば入力日前日のシリアル値に一致するまで架空の2月31日からデクリメントするという手法を取りました。月またぎ、年またぎ、うるう年に対応しています。

シリアル値を算出する関数はネットにありました。デクリメントで実在日を探し出すのは私のアイデアです。ちなみにExcelのシリアル値の起点は1900年1月1日です

こよみは本当にややこしいです。あまり関わりたくないですね。0時0分、たった1分間の作成ファイルが漏れないようにする工数としては割に合わないです。

// 日付シリアル値を算出する関数(西暦1年1月1日を0とする)
int GetDays(int y, int m, int d)
{
  if (m <= 2) // 1・2月は前年の13・14月
  {
    --y;
    m += 12;
  }
  int dy = 365 * (y - 1); // 経過年数×365日
  int c = y / 100;
  int dl = (y >> 2) - c + (c >> 2); // うるう年分
  int dm = (m * 979 - 1033) >> 5; // 1月1日からm月1日までの日数
  return dy + dl + dm + d - 1;
}

// デクリメントで実在する日を探索する関数
int GetDay(int y,int m, int d, int x){
    for (d; d >0; d--){
        int pre_day_dec = GetDays(y, m, d);
        std::cout << "d " << d << " pre_day_dec " << pre_day_dec <<endl;
        if (x == pre_day_dec){
            return d;
        }
    }
}

void execute_cb(Fl_Widget*, void*) {
    stringstream cmd;
    stringstream pre;
    const char* dir = dir_input->value();
    const char* from_char = from_input->value();
    string from_str = string(from_char);

    std::cout << "from_str " << from_str << endl;
    string day = from_str.substr(6);
    string month = from_str.substr(4, 2);
    string year = from_str.substr(0, 4);

    int days = GetDays(stoi(year), stoi(month), stoi(day));
    int predays = days - 1;

    std::cout << "days " << days << endl;
    std::cout << "predays " << predays << endl;
    std::cout << day << " " << month << " " << year <<endl;

    preDate = { 0, 0, 0, stoi(day) -1, stoi(month) -1, stoi(year)};
    preDate_t = std::mktime(&preDate);
    preDate2 = localtime(&preDate_t);

    int pre_y = preDate2->tm_year;
    int pre_m = preDate2->tm_mon +1;
    int pre_d = preDate2->tm_mday;

    std::cout << "pre_y pre_m pre_d " << pre_y << " " << pre_m << " " << pre_d <<endl;

    int day2 = GetDay(pre_y, pre_m, pre_d, predays);

    std::cout << "day2 " << day2 <<endl;

    pre << pre_y;
    pre << setw(2) << setfill('0') << pre_m;
    pre << setw(2) << setfill('0') << day2;
    
    std::cout << pre.str() << endl;
    
    cmd << "cd " << string(dir) << " && " << "find `pwd` -type file -newerct '" << pre.str() << " 23:59' ! -name '*DS_Store*'";
    std::cout << cmd.str() << endl;

    system((cmd.str()).c_str());
}

参考サイト

[C++] 39 FLTK : findコマンド生成アプリ/前日の取得 time_t型, tm構造体

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

作成中のアプリで検索期間を処理する際、開始日については入力した日の前日の日付文字列が必要になります。

time_t 型からtm構造体に変換するなどして対応しました。かなりややこしかったですし、まだ未解決の箇所もあります。

それは月をまたぐ場合です。例えば2022年3月1日の前日は2月28日ですが現時点のコードでは2月31日になってしまいます。

うるう年も含め対策が必要となります。Excelのシリアル値のようなものがあれば便利なんですが。

time_t preDate_t;
struct tm preDate;
const tm* preDate2;

void execute_cb(Fl_Widget*, void*) {
    stringstream cmd;
    stringstream pre;
    const char* dir = dir_input->value();
    const char* from_char = from_input->value();
    string from_str = string(from_char);

    std::cout << "from_str " << from_str << endl;
    string day = from_str.substr(6);
    string month = from_str.substr(4, 2);
    string year = from_str.substr(0, 4);

    std::cout << day << " " << month << " " << year <<endl;

    preDate = { 0, 0, 0, stoi(day) -1, stoi(month) -1, stoi(year)};
    preDate_t = std::mktime(&preDate);
    preDate2 = localtime(&fromDate_t);

    int pre_y = preDate2->tm_year;
    int pre_m = preDate2->tm_mon + 1;
    int pre_d = preDate2->tm_mday;

    std::cout << "pre_y pre_m pre_d " << pre_y << " " << pre_m << " " << pre_d <<endl;

    pre << preDate2->tm_year;
    pre << setw(2) << setfill('0') << preDate2->tm_mon;
    pre << setw(2) << setfill('0') << preDate2->tm_mday;
    
    std::cout << pre.str() << endl;
    
    cmd << "cd " << string(dir) << " && " << "find `pwd` -type file -newerct '" << pre.str() << " 23:59' ! -name '*DS_Store*'";
    std::cout << cmd.str() << endl;

    system((cmd.str()).c_str());
}

[C++] 38 FLTK : findコマンド生成アプリ/Fl_Native_File_Chooser

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

OSネイティブなFile Chooserが使えるとされるFl_Native_File_Chooserを試してみました。

MacOSのFile Chooserは正しく表示されましたが、ディレクトリパスの取り込みはうまくいきませんでした。Windowを閉じることもできず、仕方なくDockから終了させました。この現象確認はIntel Macでは未実施です。

#include <Fl_Native_File_Chooser.H>

void filechooser_cb(Fl_Widget*, void*) {
    // Fl_File_Chooser chooser(".",                        // directory
    //                         "*",                        // filter
    //                         Fl_File_Chooser::DIRECTORY,     // chooser type
    //                         "Dir Select");        // title
    
    Fl_Native_File_Chooser* chooser = new Fl_Native_File_Chooser();
    chooser->type(Fl_Native_File_Chooser::BROWSE_DIRECTORY);
    chooser->title("Dir Select");
    chooser->filter("*");
    chooser->directory(".");

    chooser->show();

    while(chooser->show())
        { Fl::wait(); }

    dir_input->insert(chooser->filename());
}

[C++] 37 FLTK : findコマンド生成アプリ/Fl_File_Chooser

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

ディレクトリ選択のためにFl_File_Chooserを使用します。

案外すんなりと導入できました。見た目は硬派な感じです。

#include <FL/Fl_File_Chooser.H>

void filechooser_cb(Fl_Widget*, void*) {
    Fl_File_Chooser chooser(".",      // directory
                            "*",      // filter
                            Fl_File_Chooser::DIRECTORY, // chooser type
                            "Dir");   // title
    chooser.show();

    while(chooser.shown())
        { Fl::wait(); }

    dir_input->insert(chooser.value());
}

参考サイト

[C++] 36 日時の取得 localtime関数

[M1 Mac, Big Sur 11.6.5, FLTK 1.3.8]

開発中のアプリにデフォルトとして今日の日付を表示するため、関数を用意しました。

今回は年月日だけですが年月日時分秒も可能です。

ネット情報から拝借したものをアレンジしています。

#include <iostream>
#include <iomanip>

using std::setfill; using std::setw;
using std::stringstream;

string today;
time_t t;
stringstream s;
const tm* localTime;

string getDatetimeStr() {
    t = time(nullptr);
    localTime = localtime(&t);
    s << localTime->tm_year + 1900;
    s << setw(2) << setfill('0') << localTime->tm_mon + 1;
    s << setw(2) << setfill('0') << localTime->tm_mday;
    // s << setw(2) << setfill('0') << localTime->tm_hour;
    // s << setw(2) << setfill('0') << localTime->tm_min;
    // s << setw(2) << setfill('0') << localTime->tm_sec;
    return s.str();
}

参考サイト

[Python] 96 再掲 Nikon画像ファイルの日付別取り込み

[M1 Mac, Big Sur 11.6.5, Python 3.10.0]

前のブログサイトからの引っ越しです。画像ファイルをSDカードから外部SSDへ日付振り分けしてコピーします。

Nikon Trasnferという配布アプリで日付振り分けができることを知らずにコードを書きました。今も愛用しています。

# coding: UTF-8

import shutil ,os ,glob
from PIL import Image, ExifTags
import collections

path_list = glob.glob('/NIKON D500 /DCIM/101ND500/*.JPG')

exif_datetime_list = []
for path in path_list: 
    img = Image.open(path)

    exif = { ExifTags.TAGS[k]: v for k, v in img._getexif().items() if k in ExifTags.TAGS }

    # print(exif)
    exif_datetime = [v for k, v in exif.items() if k == "DateTimeOriginal"]
    print(exif_datetime[0])

    exif_datetime_str = (exif_datetime[0].split(':')[0])[2:4] + exif_datetime[0].split(':')[1] + (exif_datetime[0].split(':')[2])[0:2]
    print(exif_datetime_str)
    exif_datetime_list.append(exif_datetime_str)

print(exif_datetime_list)

# 作成日の内訳を集計し作成日のリストを作成する
keys = collections.Counter(exif_datetime_list).keys()
print(keys)

# 作成日フォルダを作成する
for key in keys:
    try:
        os.mkdir('/photo/D500/' + str(key))
    except FileExistsError:
        pass

# 画像ファイルを各フォルダにコピーする
for path in path_list:
    
    # 画像ファイルを読み込む
    img = Image.open(path)

    exif = { ExifTags.TAGS[k]: v for k, v in img._getexif().items() if k in ExifTags.TAGS }

    exif_datetime = [v for k, v in exif.items() if k == "DateTimeOriginal"]
    
    exif_datetime_str = (exif_datetime[0].split(':')[0])[2:4] + exif_datetime[0].split(':')[1] + (exif_datetime[0].split(':')[2])[0:2]

    # ファイルパスを文字列に変換する
    path_str = str(path)

    # ファイル名を抽出する(スラッシュで分割した最後の文字列)
    filename = path_str.split('/')[-1]

    # RAWファイル名を生成する
    filename_raw = filename.split('.')[0] + '.NEF'

    # RAWファイルのコピー元を生成する
    filename_raw_src = '/NIKON D500 /DCIM/101ND500/' + str(filename_raw)
    # RAWファイルのコピー先を生成する
    filename_raw_dest = '/photo/D500/'+ str(exif_datetime_str) + '/' + str(filename_raw)

    print(f'RAWコピー元 {filename_raw_src}')
    print(f'RAWコピー先 {filename_raw_dest}\n')

    filename_jpeg_dest = '/photo/D500/'+ str(exif_datetime_str) + '/' + str(filename)

    print(f'JPEGコピー元 {path}')
    print(f'JPEGコピー先 {filename_jpeg_dest}\n')

    # JPGファイルをコピーする
    shutil.copy2(path,filename_jpeg_dest)

    # RAWファイルをコピーする
    try:
        shutil.copy2(filename_raw_src,filename_raw_dest)
    except FileNotFoundError :
        pass

# Macのデスクトップ通知
def main():
    os.system("osascript -e 'display notification \"Nikonファイルコピー\nコード実行完了\"'")

if __name__ == '__main__':
    main()