Vai al contenuto

Come si usa C in Swift? Progetto esempio


Messaggi raccomandati

Ieri notte ero particolarmente creativo e ho ricevuto l'illuminazione per la creazione di un piccolo programma che rinomina i file in batch in modo progressivo (esempio prendo 10 file li rinomina da 000 a 009).

Ma quale linguaggio usare?

Ho pensato subito al C perchè è ottimo per prototipizzare sui file (è un linguaggio molto semplice che, senza librerie aggiuntive, consente di usare semplici system call come rename o chiamare la bash con system), anche se successivamente mi sono quasi pentito della scelta (visto e considerato come si lavora male sulle stringhe).

Dopo averlo creato (come vedete molto velocemente, usando addirittura un goto per puro sfizio di farlo):

Spoiler

//
//  main.c
//  RinominaFast
//
//  Created by LiefLayer on 06/07/17.
//  Copyright © 2017 LiefLayer. All rights reserved.
//

#include <stdio.h>
#include <string.h>
#include <unistd.h>

int main(int argc, const char * argv[]) {
    char stringa[256];
    char stringaFinal[256][256];
    int ret = -1;
    char oldname[256][256];
    char oldnameWithEstension[256][256];
    char path[256];
    int index = 0;
    char myInt[3];
    char myEstension[5];
    int numElem = 0;
    
    //immetto i nomi dei file da rinominare
start:
    printf("Immetti il nome del file attuale con l'estensione, digitare stop per fermarsi!\n");
    if (fgets(oldname[index], sizeof(oldname[index]), stdin)) {
        if (oldname[index][strlen(oldname[index]) - 1] == '\n') {
            oldname[index][strlen(oldname[index]) -1] = '\0';
        }
        if(strcmp(oldname[index], "stop") != 0){
            index++;
            goto start;
        }else{
            oldname[index][0] = '\0';
            index--;
            numElem = index;
        }
    }else {
        printf("Errore 00: nome attuale troppo lungo\n");
    }
    
    index = 0;
    //immetto il path dei file da rinominare
    printf("Immetti il path del file attuale!\n");
    if (fgets(path, sizeof(path), stdin)) {
        if (path[strlen(path) - 1] == '\n') {
            path[strlen(path) -1] = '\0';
        }
    }else {
        printf("Errore 02: path troppo lungo\n");
    }
    
    while(index <= numElem){
        strcat(stringaFinal[index], path);
        index++;
    }
    
    index = 0;
    
    //prendo il nuovo nome del file
    printf("Immetti il nome principale!\n");
    if (fgets(stringa, sizeof(stringa), stdin)) {
        if (stringa[strlen(stringa) - 1] == '\n') {
            stringa[strlen(stringa) -1] = '\0';
        }
    }else {
        printf("Errore 01: nome troppo lungo\n");
    }
    
    while(index <= numElem){
        strcat(stringaFinal[index], stringa);
        index++;
    }
    
    index = 0;
    
    while(index <= numElem){
        //imposto l'index da intero a stringa
        sprintf(myInt, "%d", index);
        if(index > 9){
            strcat(stringaFinal[index], "0");
        }else{
            strcat(stringaFinal[index], "00");
        }
        //lo metto nel mio nome finale insieme al giusto numero di zeri
        strcat(stringaFinal[index], myInt);
        index++;
    }
    
    index = 0;
    
    
    //aggiungo l'estensione
    printf("Immetti l'estensione!\n");
    if (fgets(myEstension, sizeof(myEstension), stdin)) {
        if (myEstension[strlen(myEstension) - 1] == '\n') {
            myEstension[strlen(myEstension) -1] = '\0';
        }
    }else {
        printf("Errore 03: estensione errata\n");
    }
    
    while(index <= numElem){
        strcat(stringaFinal[index], myEstension);
        index++;
    }
    
    index = 0;
    
    while (index <= numElem) {
        strcat(oldnameWithEstension[index], path);
        strcat(oldnameWithEstension[index], oldname[index]);
        //rinomino il mio vecchio file con il nuovo nome compreso di estensione
        ret = rename(oldnameWithEstension[index], stringaFinal[index]);
        
        if(ret == 0){
            printf("Successo File %d %s %s %s!\n", index, oldname[index], oldnameWithEstension[index], stringaFinal[index]);
        }else{
            printf("Fallimento File %d!\n", index);
        }
        index++;
        

    }
    
    
    printf("Fine!\n");
    return 0;
}

 

 

Ho pensato che fosse un'ottima scusa per provare a utilizzare tale codice dentro un'applicazione con solo l'interfaccia grafica scritta in Swift.

 

Per farlo ho trovato molto più rapido incapsulare il mio codice C dentro una classe Objective-C (è stato così molto più semplice passare le stringhe come parametri):

Spoiler

//
//  Core.m
//  RinominaFast
//
//  Created by LiefLayer on 06/07/17.
//  Copyright © 2017 LiefLayer. All rights reserved.
//

#import <Foundation/Foundation.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#import "Core.h"

@implementation Core

char stringa[10000];
char stringaFinal[10000][10000];
char oldname[10000][10000];
char oldnameWithEstension[10000][10000];
char path[10000];
int myIndex = 0;
char myInt[10000];
char myEstension[1000];
int numElem = 0;
char commando[10000];

//immetto i nomi dei file da rinominare
- (int) coreProgramPart1 : (NSString *) daRinominare{
    strcpy(oldname[myIndex], [daRinominare UTF8String]);
    if (oldname[myIndex][strlen(oldname[myIndex]) - 1] == '\n') {
        oldname[myIndex][strlen(oldname[myIndex]) -1] = '\0';
    }
    if(strcmp(oldname[myIndex], "stop") != 0){
        myIndex++;
        return 1;
    }else{
        oldname[myIndex][0] = '\0';
        myIndex--;
        numElem = myIndex;
    }
    
    myIndex = 0;
    
    return 0;
}

- (void) coreProgramPart0 : (NSString *) daRinominare{
    strcpy(path, [daRinominare UTF8String]);
    if (path[strlen(path) - 1] == '\n') {
        path[strlen(path) -1] = '\0';
    }
    
    while(myIndex <= numElem){
        strcpy(stringaFinal[myIndex], "");
        strcat(stringaFinal[myIndex], path);
        myIndex++;
    }
    
    myIndex = 0;
}

- (void) coreProgramPart2 : (NSString *) daRinominare{
    strcpy(stringa, [daRinominare UTF8String]);
    if (stringa[strlen(stringa) - 1] == '\n') {
        stringa[strlen(stringa) -1] = '\0';
    }
    
    while(myIndex <= numElem){
        strcat(stringaFinal[myIndex], stringa);
        myIndex++;
    }
    
    myIndex = 0;
}

- (void) coreProgram {
    while(myIndex <= numElem){
        //imposto l'index da intero a stringa
        sprintf(myInt, "%d", myIndex);
        if(myIndex > 9 && myIndex <= 99){
            strcat(stringaFinal[myIndex], "0");
        }else if(myIndex > 99){
            strcat(stringaFinal[myIndex], "");
        }else{
            strcat(stringaFinal[myIndex], "00");
        }
        
        //lo metto nel mio nome finale insieme al giusto numero di zeri
        strcat(stringaFinal[myIndex], myInt);
        myIndex++;
    }
    
    myIndex = 0;
    
}

- (void) coreProgramPart3 : (NSString *) daRinominare{
    strcpy(myEstension, [daRinominare UTF8String]);

    //aggiungo l'estensione
    if (myEstension[strlen(myEstension) - 1] == '\n') {
        myEstension[strlen(myEstension) -1] = '\0';
    }
    
    while(myIndex <= numElem){
        strcat(stringaFinal[myIndex], myEstension);
        myIndex++;
    }
    
    myIndex = 0;
    
    //costruisce e passa il comando da eseguire su bash
    while (myIndex <= numElem) {
        strcat(oldnameWithEstension[myIndex], path);
        strcat(oldnameWithEstension[myIndex], oldname[myIndex]);
        //rinomino il mio vecchio file con il nuovo nome compreso di estensione
        int ret = rename(oldnameWithEstension[myIndex], stringaFinal[myIndex]);
        
        if(ret == 0){
            printf("Successo File %d \n", myIndex);
        }else{
            printf("Fallimento File %d \n", myIndex);
        }
        
        myIndex++;
        
    }
    myIndex = 0;
    
    while (myIndex <= numElem) {
        strcpy(oldnameWithEstension[myIndex], "");
        strcpy(stringaFinal[myIndex], "");
        strcpy(oldname[myIndex], "");
        myIndex++;
    }
    myIndex = 0;
    
    
    
    printf("Fine!\n");
}


@end

 

 

Ovviamente questo è il codice finale, in mezzo ci sono stati tanti passaggi.

 

Ho così creato il .h per questa classe

Spoiler

//
//  Core.h
//  RinominaFast
//
//  Created by LiefLayer on 06/07/17.
//  Copyright © 2017 LiefLayer. All rights reserved.
//

#ifndef Core_h
#define Core_h

#import <Foundation/Foundation.h>

@interface Core : NSObject

- (int) coreProgramPart1: (NSString *) daRinominare;

- (void) coreProgramPart0: (NSString *) daRinominare;

- (void) coreProgramPart2: (NSString *) daRinominare;

- (void) coreProgram;

- (void) coreProgramPart3: (NSString *) daRinominare;

@end

#endif /* Core_h */

 

e come si può vedere non ho dovuto modificare praticamente nulla del mio codice C vero e proprio.

Ho dovuto infatti solo aggiungere il wrapper della classe e dei metodi Objective-C.

Ovviamente ho dovuto anche creare un piccolo file bridge

NomeProgetto-Bridging-Header.h

dove ho solo scritto

#import "Core.h"

Successivamente ho poi modificato il codice per adattarsi alla mia interfaccia grafica scritta in Swift (che ovviamente prevede la selezione dei file attraverso una finestra di selezione dei file):

Spoiler

//
//  ViewController.swift
//  RinominaFast
//
//  Created by LiefLayer on 06/07/17.
//  Copyright © 2017 LiefLayer. All rights reserved.
//

import Cocoa

class ViewController: NSViewController {

    var instanceOfCore: Core = Core()
    var retPart1: Int = -1
    var daRinominare: String! = "";

    @IBOutlet var stringInInput: NSTextField!
    
    @IBOutlet var filename_field: NSTextField!
    
    var path = ""
    
    override func viewDidLoad() {
        super.viewDidLoad()

        // Do any additional setup after loading the view.
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }
    
    @IBAction func browseFile(_ sender: Any) {
        
        let dialog = NSOpenPanel();
        
        dialog.title                   = "Choose files";
        dialog.showsResizeIndicator    = true;
        dialog.showsHiddenFiles        = false;
        dialog.canChooseDirectories    = true;
        dialog.canCreateDirectories    = true;
        dialog.allowsMultipleSelection = true;
        
        if (dialog.runModal() == NSModalResponseOK) {
            let result = dialog.urls // Pathname of the file
            var pathMyFile = ""
            var toIndex = 0;
            path = ""
            filename_field.stringValue = ""
            
            
            while(toIndex < result.count){
                path = result[toIndex].path
                filename_field.stringValue = filename_field.stringValue + path + "'"
                let myUrl = URL(fileURLWithPath: path)
                let dirUrl = myUrl.deletingLastPathComponent()
                pathMyFile = dirUrl.path + "/"
                path = result[toIndex].lastPathComponent
                retPart1 = Int(instanceOfCore.coreProgramPart1(path));
                path = result[toIndex].pathExtension
                toIndex += 1;
            }
            retPart1 = Int(instanceOfCore.coreProgramPart1("stop"));
            instanceOfCore.coreProgramPart0(pathMyFile)
            
        } else {
            // User clicked on "Cancel"
            return
        }
        
    }

    @IBAction func launchPart2(_ sender: Any) {
        if(retPart1 != 0){
            return;
        }
        daRinominare = stringInInput.stringValue
        instanceOfCore.coreProgramPart2(daRinominare)
        instanceOfCore.coreProgram();
        instanceOfCore.coreProgramPart3("."+path)
    }

}

 

 

 

Ovviamente il programma non è per nulla ottimizzato e potrebbe avere dei bug (non mi prendo responsabilità in caso perdiate dei dati, quindi non utilizzate questo programma su dati importanti).

Però l'obbiettivo primario è stato raggiunto.

Il programma è attualmente una sorta di MVC in cui la View (l'interfaccia grafica) è scritta in Swift e non ha alcuna funzionalità se non quella di passare l'input al Model (la classe C wrappata all'interno dell'Objective-C) attraverso il Controller (il bridge).

Tutte le funzionalità di base sono svolte backend dal codice C originale, solo l'interfaccia grafica è in Swift.

 

Questo mini-tutorial si conclude qui, spero che sia stato utile a qualcuno.

Se volete più informazioni vi consiglio questo link stackoverflow:

Concludo dicendo solo che per usare le funzioni C in Swift direttamente (senza Objective-C) si può fare in questo modo:

funzione_c("World".cStringUsingEncoding(NSUTF8StringEncoding))

lo scrivo perchè in linea di massima è l'unica cosa non proprio intuitiva ("come usare un oggetto C se in C gli oggetti non esistono? Semplice ne hanno creato uno").

 

Il vantaggio principale è ovviamente che se avete del codice C, C++ o Objective-C già scritto.... Oppure vi piace scrivere in linguaggi diversi. Potete farlo.

In linea di massima potreste persino non saper scrivere codice Swift (e la costruzione dell'interfaccia grafica è particolarmente semplice).

Link al commento
Condividi su altri siti

Archiviato

Questa discussione è archiviata e chiusa a future risposte.

×
×
  • Crea Nuovo...