Skip to main content

· 5 min read

Purpose#

The purpose of the Converter pattern is to provide a generic, common way of bidirectional conversion between corresponding types, allowing a clean implementation in which the types do not need to be aware of each other.

In plain words, the Converter pattern makes it easy to map instances of one class into instances of another class.

We're making use of Converter to map models when they are propagating between layers (API, Data, Domain, Presentation) to make them convenient to work with on a specific layer and do not spread extra dependencies to other layers.

DIP#

Conversion is here to comply with dependency inversion principle. Domain layer only know about itself. UI layer needs to convert UI model into Domain DTO to deal with Domain layer. Data layer needs to convert Data model into Domain dto to deals with Domain layer.

SRP#

We want to extract conversion methods to comply with Single Responsibility Principle. Putting conversion logic in its own scope, makes it easier to test. Extracting conversion logic from other classes, ensure that they focus on their business and not in conversion logic.

Implementation#

Actually, there are different uses cases that we should handle that would trigger different implementations

Simple extension method#

For the simpler cases, it is advised to stick with YAGNI principle and have only an extension method in a file named <source type>To<destination type>Converter.kt because you aren’t gonna need a class.

fun MyModel.toMyOtherModel() = MyOtherModel(   attr1 = id,   attr2 = name,   //...)
  • But I need to handle lists of items !

    Kotlin is here to help thanks to the map{} extension to collection classes

// Given this converter methodfun MyModel.toMyOtherModel(): MyOtherModel {}// And the fact that we have a list of objects to convertval myModels = listOf(myModel1, myModel2, myModel3)
// Convertion could be done with map extension method.val myOtherModels = myModels.map { it.toMyOtherModel() }

❌ Do not call extension method from domain layer.

Simple class#

You can create a converter class if you want to use Injection features or if you need additional contributors. You should always avoid to use kotlin objects because using a Singleton is often a code smell. It is a ressource killer because never destoyed. They are hiding dependencies and states and they are making your application more coupled.

Example#

👉 Create the converter class that can be injected thanks to @Inject

class ATypeToAnotherTypeConverter @Inject constructor()    fun convert(from: AType): AnotherType = when (this) {            AType.A-> AnotherType.A            AType.B-> AnotherType.B        }}

We need an additional contributor#

Sometimes you’ll need an additional contributor like a LocaleProvider or a DateProvider

👉 Inject them in the converter class

class DomainDtoToUiModel @Inject constructor(    // here timezone is dynamic, it is retrieved from a provider    timeZoneProvider: TimeZoneProvider) {
    // Here date format is static. To make is dynamic, format can be injected in constructor.        private val simpleDateFormat = SimpleDateFormat(/*...*/)
      fun convert(dto: DomainDto): UiModel {        return UiModel(            displayDate = simpleDateFormat.format(dto.currentDate, timeZoneProvider.getLocalTimeZone())        )        }{

We need a converter with additional parameters#

Most of the time, it is an unexpected case. Converters have 1:1 relationship.

💡 If logic begins to be complex, or you’re creating several implementations of an interface, you should consider using a Factory or a Builder instead.

You might want to convert a domain DTO in a model of UI or data layer, then use the last model along a factory to create more complex instance.

✅ Do#

  • Place converter in UI or Data layer (along ViewModel or DataSource)
  • Make converter as simple as possible

❌ Don’t#

  • 🧨 Conversion method can crash
  • Call converter from Domain layer
  • Implement complex logic
  • Extend very common classes like String, Long or Int

Naming#

Class#

<source type>To<destination type>Converter, e.g.:

  • LocalCardToCardConverter (LocalCard 👉 Card)
  • RequestToLocalRequestConverter (Request 👉 LocalRequest)

đź’ˇ It should be clear what types converters are converting. This is the reason why the source type and the destination type are mentioned.

Extension method#

The extension method used for converting the extended object into the target one should be named : fun Model.toNameOfTargetedClass(){}

It should belong to a file named <source type>To<destination type>Converter.kt

đź’ˇ The destination type of the converter extension method should be crystal clear. A model can also be converted in several kind of classes. This is the reason why methods like .toDomainModel() or .toDataModel() are not encouraged.

Given this data classes:

// Data layerdata class RemoteTypeOfMyObject()
// Domain layerdata class MyAwesomeData()
// UI layerdata class AwesomeUiState()

Then we should have these extension methods

// Data layerfun RemoteTypeOfMyObject.toMyAwsomeData(): MyAwesomeDatafun MyAwesomeData.toRemoteTypeOfMyObject(): RemoteTypeOfMyObject
// UI Layerfun AwsomeUiState.toMyAwsomeData(): MyAwesomeDatafun MyAwesomeData.toAwesomeUiState(): AwesomeUiState

File#

Converter class or methods should be placed into their belonging layer, not in the domain one.

⚠️ Domain layer should not know about classes that are in UI or in Data layer.

root  |- Data  |    |-...- RemoteTypeOfMyObject.kt  |    |-...- RemoteTypeOfMyObjectConverter.kt  <- Converter is here  |  |- Domain  |     /!\ No converters here /!\  |  |- UI      |-...- AwsomeUiState.kt      |-...- AwsomeUiStateConverter.kt <- Converter is here

Layers#

Data#

Converters in data layer should be called from data sources. Repositories are dealing with business models.

A source is part of the external world : everything that you don’t own and that you want to scope into the smallest possible part. No-one should know about your source implementation details except the source itself. So it should be responsible to expose a model that the consumer can undestand without having to know the implementation details of that source

UI#

Converters in UI layer should be called from ViewModels. They are the bridge between UI model and business rules (implemented in UseCases)

· One min read

One of the most anoying thing I meet when I am coding software, is the « No new line at end of file » warning.

manifest

For this, I have written a little script based of my research on google. I want to rewrite all text files tracked by git and ensure that a new line is written at the end of file.

#!/bin/bash
# list all files tracked by gitfor file in `git ls-files -c -m`;do  # Test that file does exist  if [ -f $file ]; then    # Test that file is not a binary one    grep -Iq . "$file"    if [ $? -eq 0 ]; then      # Add a new line      sed -i '$a\' $file    fi  fidone

You can execute it from a git repository and commit the result.

· 7 min read

In some applications, I need to store my logs in a file aside of traditional logcat. For this, I am making use of Timber library. Because I don’t want to make my device full of logs, I wanted to use circular log files so that I can control the maximum amount of bytes taken by log data. To achieve this, I will use java Logger API to implement a new Timber.Tree. I also want some feature like log formatting and filtering.

All of this is implemented by Treessence library.

Log filtering#

To implement filtering an interface is defined :

public interface Filter {
    /**     * @param priority Log priority.     * @param tag      Tag for log.     * @param message  Formatted log message.     * @param t        Accompanying exceptions.     * @return {@code true} if the log should be skipped, otherwise {@code false}.     * @see timber.log.Timber.Tree#log(int, String, String, Throwable)     */    boolean skipLog(int priority, String tag, String message, Throwable t);
    boolean isLoggable(int priority, String tag);}

Priority filtering is provided by an implementation of this interface

public class PriorityFilter implements Filter {
    private final int minPriority;
    public PriorityFilter(int minPriority) {        this.minPriority = minPriority;    }
    @Override    public boolean skipLog(int priority, String tag, String message, Throwable t) {        return priority < minPriority;    }
    @Override    public boolean isLoggable(int priority, String tag) {        return priority >= minPriority;    }
    public int getMinPriority() {        return minPriority;    }}

We can now create our base class extending Timber.DebugTree

public class PriorityTree extends Timber.DebugTree {
    private final PriorityFilter priorityFilter;    private Filter filter = NoFilter.INSTANCE;
    /**     * @param priority priority from witch log will be logged     */    public PriorityTree(int priority) {        this.priorityFilter = new PriorityFilter(priority);    }
    /**     * Add additional {@link Filter}     *     * @param f Filter     * @return itself     */    public PriorityTree withFilter(@NotNull Filter f) {        this.filter = f;        return this;    }
    @Override    protected boolean isLoggable(int priority) {        return isLoggable("", priority);    }
    @Override    public boolean isLoggable(String tag, int priority) {        return priorityFilter.isLoggable(priority, tag) && filter.isLoggable(priority, tag);    }
    public PriorityFilter getPriorityFilter() {        return priorityFilter;    }
    public Filter getFilter() {        return filter;    }
    /**     * Use the additional filter to determine if this log needs to be skipped     *     * @param priority Log priority     * @param tag      Log tag     * @param message  Log message     * @param t        Log throwable     * @return true if needed to be skipped or false     */    protected boolean skipLog(int priority, String tag, @NotNull String message, Throwable t) {        return filter.skipLog(priority, tag, message, t);    }}

This class can filter on two parameters :

  • First parameter is obviously log priority. This is done thanks to PriorityFilter instance.
  • Second parameter is an additional Filter instance that can be provided by caller.

## Log formatting

Log formatting is obtained thanks to a Formatter class whose interface is defined as follow

public interface Formatter {
    String format(int priority, String tag, String message);}

Each formatter can display log to a defined format. For instance, logcat format is "MM-dd HH:mm:ss:SSS {priority}/{tag}({thread id}) : {message}\n". Another format would be "{tag} : {message}".

Because we want to log in a file what we get in logcat, then we need to implement a logcat formatter

public class LogcatFormatter implements Formatter {
    public static final LogcatFormatter INSTANCE = new LogcatFormatter();    private static final String SEP = " ";
    private final HashMap<Integer, String> prioPrefixes = new HashMap<>();
    private LogcatFormatter() {        prioPrefixes.put(Log.VERBOSE, "V/");        prioPrefixes.put(Log.DEBUG, "D/");        prioPrefixes.put(Log.INFO, "I/");        prioPrefixes.put(Log.WARN, "W/");        prioPrefixes.put(Log.ERROR, "E/");        prioPrefixes.put(Log.ASSERT, "WTF/");    }
    @Override    public String format(int priority, String tag, @NotNull String message) {        String prio = prioPrefixes.get(priority);        if (prio == null) {            prio = "";        }        return TimeUtils.timestampToDate(System.currentTimeMillis(), "MM-dd HH:mm:ss:SSS")               + SEP               + prio               + (tag == null ? "" : tag)               + "(" + Thread.currentThread().getId() + ") :"               + SEP               + message               + "\n";    }}

Priority class can then be extended to add format functionality

/** * Base class to format logs */public class FormatterPriorityTree extends PriorityTree {    private Formatter formatter = getDefaultFormatter();
    public FormatterPriorityTree(int priority) {        super(priority);    }
    /**     * Set {@link Formatter}     *     * @param f formatter     * @return itself     */    public FormatterPriorityTree withFormatter(Formatter f) {        this.formatter = f;        return this;    }
    /**     * Use its formatter to format log     *     * @param priority Priority     * @param tag      Tag     * @param message  Message     * @return Formatted log     */    protected String format(int priority, String tag, @NotNull String message) {        return formatter.format(priority, tag, message);    }
    /**     * @return Default log {@link Formatter}     */    protected Formatter getDefaultFormatter() {        return NoTagFormatter.INSTANCE;    }
    @Override    protected void log(int priority, String tag, @NotNull String message, Throwable t) {        super.log(priority, tag, format(priority, tag, message), t);    }}

File logging#

We have seen how to filter and format logs. We can now start logging in file.

For this we need a java.util.logging.Logger instance. It will be used in conjunction with java.util.logging.FileHandler that do actual file logging. We will see how to create a Logger instance later.

public class FileLoggerTree extends FormatterPriorityTree {    private final Logger logger;
    private FileLoggerTree(int priority,                           Logger logger) {        super(priority);        this.logger = logger;    }}

To activate logcat formatting by default, getDefaultFormatter() method is overridden

@Overrideprotected fr.bipi.tressence.common.formatter.Formatter getDefaultFormatter() {    return LogcatFormatter.INSTANCE;}

We need to convert logcat level to java.util.logging.Level

private Level fromPriorityToLevel(int priority) {    switch (priority) {        case Log.VERBOSE:            return Level.FINER;        case Log.DEBUG:            return Level.FINE;        case Log.INFO:            return Level.INFO;        case Log.WARN:            return Level.WARNING;        case Log.ERROR:            return Level.SEVERE;        case Log.ASSERT:            return Level.SEVERE;        default:            return Level.FINEST;    }}

Logging is done by this method

@Overrideprotected void log(int priority, String tag, @NotNull String message, Throwable t) {    if (skipLog(priority, tag, message, t)) {        return;    }
    logger.log(fromPriorityToLevel(priority), format(priority, tag, message));    if (t != null) {        logger.log(fromPriorityToLevel(priority), "", t);    }}

It is logging in using java.utils.logging API with log level conversion and logcat formatting

We haven’t seen how to provide the right logger. Let see how to configure it.

A builder class is defined to create a FileLoggerTree instance. This builder contains some default:

public static class Builder {    // 1 mb byte of data    private static final int SIZE_LIMIT = 1048576;    // Max 3 files for circular logging    private static final int NB_FILE_LIMIT = 3;
    // Base filename.    // log index will be appended so actual file name will be    // "log.0" or "log.1"    // To parametrize where index is put, "%g" can be placed    // in file name. For instance "log%g.logcat" will give    // "log0.logcat", "log1.logcat" and so on    private String fileName = "log";    // Directory where files are stored    private String dir = "";    // Min priority to log from    private int priority = Log.INFO;    private int sizeLimit = SIZE_LIMIT;    private int fileLimit = NB_FILE_LIMIT;    // append log to already existing log file    private boolean appendToFile = true;
...}

java.util.logging.Logger are created and managed by java.util.logging.LogManager. To bypass this a simple static class is used

/** * Custom logger class that has no references to LogManager */private static class MyLogger extends Logger {
    /**     * Constructs a {@code Logger} object with the supplied name and resource     * bundle name; {@code notifyParentHandlers} is set to {@code true}.     * <p/>* Notice : Loggers use a naming hierarchy. Thus "z.x.y" is a child of "z.x".     *     * @param name the name of this logger, may be {@code null} for anonymous     *             loggers.     */    MyLogger(String name) {        super(name, null);    }
    public static Logger getLogger(String name) {        return new MyLogger(name);    }}

Creation of java.util.logging.Logger can start

public FileLoggerTree build() throws IOException {  // Log file path  String path = FileUtils.combinePath(dir, fileName);  // File handler that is performing file logging  FileHandler fileHandler;  // Our custom logger  Logger logger = MyLogger.getLogger(TAG);  // We force level to ALL because priority filtering is  // done by our Tree implementation  logger.setLevel(Level.ALL);  // File handler can now be created  fileHandler = new FileHandler(path, sizeLimit, fileLimit,  appendToFile);  // Formating is done by our Tree implementation  fileHandler.setFormatter(new NoFormatter());  // Configure java Logger  logger.addHandler(fileHandler);  // finally we got here !  return new FileLoggerTree(priority, logger);}

Full code of FileLoggerTree is here

This tree can then be planted like this

FileLoggerTree fileTree = FileLoggerTree.Builder()        .withFileName("log%g.logcat")        .withMinPriority(Log.VERBOSE)        .build()Timber.plant(fileTree)

Thanks for reading this. Full source code is available here

· 4 min read

I wanted to play more with Kotlin and I wanted to publish KGeoGson lib to a remote maven repo.

I was following gradle guide to build my kotlin project with Kotlin DSL.

Bootstrap Kotlin project#

Create project directory with files of your library you want to build and publish. You may have a directory structure like the following:

project├── build.gradle.kts├── settings.gradle.kts└── my-kotlin-library    ├── build.gradle.kts    └── src        ├── main        │   └── kotlin        │       └── org        │           └── example        │               └── MyLibrary.kt        └── test            └── kotlin                └── org                    └── example                        └── MyLibraryTest.kt

Setup#

Root build.gradle.kts

plugins {   `build-scan`}
buildScan {   termsOfServiceUrl = "https://gradle.com/terms-of-service"   termsOfServiceAgree = "yes"
   publishAlways()}
val clean by tasks.creating(Delete::class) {   delete(rootProject.buildDir)}

In this file, « build-scan » plugin is activated and a clean task is added. That’s all for it

settings.gradle.kts

include("lib")

In this file, we define our modules

lib/build.gradle.kts

First we are adding some plugins

plugins {   // Add Kotlin plugin to build our Kotlin lib   kotlin("jvm") version "1.3.21"   // Get version from git tags   id("fr.coppernic.versioning") version "3.1.2"   // Documentation for our code   id("org.jetbrains.dokka") version "0.9.17"   // Publication to bintray   id("com.jfrog.bintray") version "1.8.4"   // Maven publication   `maven-publish`}

Then we are defining dependencies and their repositories for our code

repositories {   jcenter()   mavenCentral()}
dependencies {   implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7")   implementation("com.google.code.gson:gson:2.8.5")
   testCompile("junit:junit:4.12")}

We need some more tasks to add sources and javadoc to our lib. We are starting by configuring dokka task:

tasks {   dokka {       outputFormat = "html"       outputDirectory = "$buildDir/javadoc"       moduleName = rootProject.name   }}

We can then bundle documentation into a jar

val dokkaJar by tasks.creating(Jar::class) {   group = JavaBasePlugin.DOCUMENTATION_GROUP   description = "Assembles Kotlin docs with Dokka"   archiveClassifier.set("javadoc")   from(tasks.dokka)   dependsOn(tasks.dokka)}

We are creating another jar containing sources

val sourcesJar by tasks.creating(Jar::class) {   archiveClassifier.set("sources")   from(sourceSets.getByName("main").allSource)}

At this stage, you are able to compile your lib. The most important part of this article begins. Let’s see how publication is working. It is very important to configure pom.xml file of maven artifact in a right manner. Otherwise you will not be able to submit your library into JCenter repo.

Let’s start configuring base of maven publish plugin

val artifactName = "libname"val artifactGroup = "org.example"
publishing {   publications {       create<MavenPublication>("lib") {           groupId = artifactGroup           artifactId = artifactName           // version is gotten from an external plugin           version = project.versioning.info.display           // This is the main artifact           from(components["java"])           // We are adding documentation artifact           artifact(dokkaJar)           // And sources           artifact(sourcesJar)       }   }}

Now we need to add information about package in pom.xml file. You can edit pom.xml with pom.withXml { code

val pomUrl = "..."val pomScmUrl = "..."val pomIssueUrl = "..."val pomDesc = "..."val pomScmConnection = ""..."val pomScmDevConnection = "..."
val githubRepo = "..."val githubReadme = "..."
val pomLicenseName = "The Apache Software License, Version 2.0"val pomLicenseUrl = "http://www.apache.org/licenses/LICENSE-2.0.txt"val pomLicenseDist = "repo"
val pomDeveloperId = "..."val pomDeveloperName = "..."

publishing {   publications {       create<MavenPublication>("lib") {           [...]
           pom.withXml {               asNode().apply {                   appendNode("description", pomDesc)                   appendNode("name", rootProject.name)                   appendNode("url", pomUrl)                   appendNode("licenses").appendNode("license").apply {                       appendNode("name", pomLicenseName)                       appendNode("url", pomLicenseUrl)                       appendNode("distribution", pomLicenseDist)                   }                   appendNode("developers").appendNode("developer").apply {                       appendNode("id", pomDeveloperId)                       appendNode("name", pomDeveloperName)                   }                   appendNode("scm").apply {                       appendNode("url", pomScmUrl)                       appendNode("connection", pomScmConnection)                   }               }           }       }   }}

Now that your maven publication is well configured, you can configure bintray plugin

bintray {   // Getting bintray user and key from properties file or command line   user = if (project.hasProperty("bintray_user")) project.property("bintray_user") as String else ""   key = if (project.hasProperty("bintray_key")) project.property("bintray_key") as String else ""
   // Automatic publication enabled   publish = true
   // Set maven publication onto bintray plugin   setPublications("lib")
   // Configure package   pkg.apply {       repo = "maven"       name = rootProject.name       setLicenses("Apache-2.0")       setLabels("Gson", "json", "GeoJson", "GPS", "Kotlin")       vcsUrl = pomScmUrl       websiteUrl = pomUrl       issueTrackerUrl = pomIssueUrl       githubRepo = githubRepo       githubReleaseNotesFile = githubReadme
       // Configure version       version.apply {           name = project.versioning.info.display           desc = pomDesc           released = Date().toString()           vcsTag = project.versioning.info.tag       }   }}

Here we is ! Happy publication !

· 2 min read

On an Android project, I am using android-material-stepper from StepStone. I am also using dependency injection with dagger.

I was facing the following problem : How to properly inject Fragments without the need to create a DaggerComponent for each of them ?

Providers are here to help. This is how I am doing

class StepFragmentBL @Inject constructor() : Fragment(), Step, BlView {
    @Inject    lateinit var blPresenter: BlPresenter
    ...}

This is one of the Fragment I am creating. We can see the @Inject constructor with @Inject lateinit var that shows that this object is injectable by dagger. It is also implementing Step to be used by StepperLayout and implements another interface.

class BlPresenter @Inject constructor
BlPresenter is a simple injectable class
@Singleton@Component(modules = [(DeliveryModule::class)])interface DeliveryComponents {
    fun inject(deliveryActivity: DeliveryActivity)}

This is the component injecting my Activity

@Moduleclass DeliveryModule(private val activity: DeliveryActivity) {
    @Named("Activity")    @Provides    internal fun providesContext(): Context {        return activity    }
    @Provides    fun providesFragmentManager(): FragmentManager {        return activity.supportFragmentManager    }}
class DeliveryActivity : AppCompatActivity() {
    @Inject    lateinit var deliveryPresenter: DeliveryPresenter
    private lateinit var deliveryComponents: DeliveryComponents
    override fun onCreate(savedInstanceState: Bundle?) {        super.onCreate(savedInstanceState)        setContentView(R.layout.activity_home)
        di()        ui()    }
    private fun di() {        deliveryComponents = DaggerDeliveryComponents.builder()                .deliveryModule(DeliveryModule(this))                .build()        deliveryComponents.inject(this)    }
    private fun ui() {        Timber.v(Info.getMethodName())        stepperLayout.adapter = deliveryPresenter.stepperAdapter    }}

We are creating the component into the Activity here. Then, presenter for this activity is also injected

class DeliveryPresenter @Inject constructor() {
    @Inject    lateinit var stepperAdapter: PickingStepperAdapter
}

We are arriving to a StepperAdapter instance, also injected

private const val NB_FRAG = 2
class PickingStepperAdapter @Inject constructor(        fm: FragmentManager,        @Named("Activity") context: Context) :            AbstractFragmentStepAdapter(fm, context) {
    /**    * Provider for Fragment    */    @Inject    lateinit var blFragmentProvider: Provider<StepFragmentBL>
    /**    * Provider for Fragment    */    @Inject    lateinit var pdtFragmentProvider: Provider<StepFragmentPdt>
    override fun getCount(): Int {        return NB_FRAG    }
    override fun createStep(position: Int): Step {        Timber.v("${Info.getMethodName()} $position")
        return when (position) {            0 -> blFragmentProvider.get()            else -> pdtFragmentProvider.get()        }    }}

Here, we are just using

@Inject    lateinit var blFragmentProvider: Provider<StepFragmentBL>

to tell dagger to create the code that will provide an instance of fragment each time blFragmentProvider.get() will be called. Indeed, this is called here

return when (position) {            0 -> blFragmentProvider.get()            else -> pdtFragmentProvider.get()        }

Like this, dagger is injecting fragment for us, and we don’t have to care about objects creation

· One min read
  • Extraire la clĂ© contenue dans le fichier pk8 et la mettre en clair dans un fichier pem
openssl pkcs8 -inform DER -nocrypt -in key.pk8 -out key.pem
  • Regrouper la clĂ© et le certificat dans un fichier p12 temporaire
openssl pkcs12 -export -in certificate.pem -inkey key.pem -out platform.p12 -password pass:android -name AndroidDebugKey
  • GĂ©nĂ©rer le fichier jks Ă  partir du p12 (keytool est un outil du framework android)
keytool -importkeystore -deststorepass android -destkeystore platform.jks -srckeystore platform.p12 -srcstoretype PKCS12 -srcstorepass android
  • VĂ©rifier le fichier
keytool -list -v -keystore platform.jks

· One min read

Pour protéger les classes contre la copie, il suffit de déclarer le constructeurs de copie en private. Une macro peut être définie car le principe est le même pour toutes les classes.

#define DECLARE_NO_COPY_CLASS(classname)      private:                                      classname(const classname&);#endif

De même pour les protéger contre l’assignation, on peut déclarer la surcharge de l’opérateur = comme étant private.

#define DECLARE_NO_ASSIGN_CLASS(classname)      private:                                        classname& operator=(const classname&);#endif

· One min read

L’utilisation d’un assert statique peut être utile dans le cas ou une condition doit être testée au moment de la compilation. Par exemple la taille d’un tableau. Le principe est d’utiliser une macro pour définir une expression. Si le test est valide alors l’expression générée l’est aussi, sinon elle résulte en une erreur de compilation.

#define STATIC_ASSERT(cond)    typedef int ERROR_##__LINE__[(cond) ? 1 : -1]

Ici, on utilise typedef pour définir un type tableau. Si la condition est valide alors la taille du tableau est positive. Sinon elle est négative ce qui résulte en une erreur de compilation.

Exemple d’utilisation :

STATIC_ASSERT(sizeof(object1) == sizeof(object2));

· 2 min read

Préambule#

Le mutex locker fait partie du concept de RAII. Cette acronyme vient de l’anglais Resource Acquisition Is Initialization. C’est une technique de programmation qui permet de s’assurer que la ressource acquise sera bien libérée à la fin de la vie d’un objet.

Principe#

Appliqué au mutex, ce principe consiste à bloquer un mutex lors de la création d’un objet et de le libérer lors de sa destruction. On peut imaginer la création d’un objet spécifique : le MutexLocker. Il faudrait qu’il puisse prendre un mutex lors de sa création et le libérer lors de sa destruction. Son constructeur va donc prendre le mutex et son destructeur le détruire.

class MutexLocker {    public :        MutexLocker(Mutex& mutex): m_mutex(mutex) {            m_mutex.Lock();        }        virtual ~MutexLocker(){            m_mutex.Unlock();        }
    private:        Mutex& m_mutex;};

Son utilisation procède comme suit :

{    Mutex mutex;    MutexLocker locker(mutex);    /* Maintenant le mutex est pris. Le code critique peut être exécuté. */}

Conclusion#

Son utilisation est justifié quand des exceptions peuvent arriver à tout moment. A ce moment, le mutex est assuré d’être libéré à la fin de la portée du code lorsque le locker est détruit. D’une manière générale, il est bon de l’utiliser tout le temps pour éviter de tomber dans des cas d’erreur difficiles à détecter.

· 3 min read

Présentation#

Le singleton est un patron de conception (design pattern) dont le but est de restreindre l’instanciation d’une classe à un nombre défini d’objets. Il est utilisé lorsque l’on a besoin que d’un seul objet pour coordonner des actions dans un système. Par exemple, le singleton va être utilisé comme gestionnaire de fenêtre, gestionnaire de systèmes de fichiers ou d’imprimantes. Le singleton va être disponible de manière globale dans un package et sera utilisé comme étant un point d’accès.

Le singleton est une classe qui va s’instancier elle-même. Ses constructeurs seront donc définis comme étant privés. Elle met à disposition une méthode statique pour récupérer son instance. Cette méthode vérifie qu’une seule instance existe ou en crée une si elle n’existe pas encore.

Une attention particulière sera donnée dans les environnements multi-threadés. Si deux thread essaient d’accéder à l’objet, un seul devra l’instancier tandis que le deuxième obtiendra une référence sur l’objet nouvellement créé.

Implémentation en C++#

template <typename T> class Singleton{    public:        static T* Get() {            if(null == m_intance) {                m_instance = new T;            }            return m_instance;        }
    protected:        Singleton(){};        virtual ~Singleton(){};
    private :        static T* m_instance;};

Les classes peuvent ensuite hériter de Singleton. Elles seront instanciées par la méthode héritée Get().

lass Eur : public Singleton<Eur>{    friend class Singleton<Eur>;};
func(){    Eur* obj = Eur::Get();};

Multi-Threading#

Dans un environnement multi-tâche, il est important de protéger le code d’instanciation pour éviter d’avoir plusieurs références vers le Singleton lorsque celui-ci est appelé depuis plusieurs threads différents.

La solution peut ĂŞtre comme suit :

template <typename T> class Singleton{    public:        static T* Get() {            m_mutex.Lock();            if(null == m_intance) {                m_instance = new T;            }            m_mutex.unlock();            return m_instance;        }
    protected:        Singleton(){};        virtual ~Singleton(){};
    private :        static T* m_instance;        static Mutex m_mutex;};

Ici Mutex est une classe qui initialise le mutex dans son constructeur par défaut et présente les méthodes Lock() et Unlock() pour locker et délocker le mutex.

Le problème de cette solution est que le mutex est locké à chaque appel de Get(). Ceci peut être coûteux en nombre d’instruction en comparaison avec un simple test pour savoir si m_instance existe. D’autant plus que le Singleton n’est instancié qu’une seule fois pendant la durée de vie du programme.

Une proposition d’optimisation serait de tester une première fois si l’instance existe sans locker le mutex puis de tester une seconde fois sous protection dans le cas ou l’instance n’existe pas. Ceci ajoute un test dans le cas ou l’instance n’existe pas, mais enlève tout appel au mutex dans le cas ou elle existe.

template <typename T> class Singleton{    public:        static T* Get() {            if(null == instance)            {                m_mutex.Lock();                if(null == m_intance) {                    m_instance = new T;                }                m_mutex.unlock();            }            return m_instance;        }
    protected:        Singleton(){};        virtual ~Singleton(){};
    private :        static T* m_instance;        static Mutex m_mutex;};

Cette solution est valide car les accès concurrents en lecture ne sont pas pénalisant. En effet, le Singleton est le même pour tous les threads.