Outils pour utilisateurs

Outils du site


lib:boost

Windows

Compilation

git clone https://github.com/boostorg/boost.git
git checkout boost-1.69.0 --recurse-submodules
# C'est celui dans le dossier boost pas boost\tools\build
bootstrap.bat
b2 -j4 toolset=msvc-14.0 address-model=64 architecture=x86 threading=multi --build-type=complete variant=debug,release install --prefix=boost.install

Compilation pour Visual Studio 2015 et pour des programmes en 64 bits (address-model). architecture vaut x86 que ce soit pour address-model à 32 ou 64.

Il n'y a pas link ni runtime-link de défini. Les deux versions static et shared seront compilés.

On génère simultanément la version de release et debug.

Nommage de la dll

boost::asio::execution_context

L'exécuteur a les caractéristiques suivantes :

  • Fonctionnement entre thread, pas entre application via TCP,
  • Toujours en fonctionnement. En attente s'il n'a plus de travail à faire.
cpp/boost/executor/executor.h
#pragma once
 
#include <functional>
#include <memory>
 
class PriorityScheduler final
{
 public:
  // Classe satisfaisant l'interface Executor.
  class ExecutorType final
  {
   public:
    ExecutorType(PriorityScheduler* ctx, int pri);
    ~ExecutorType();
 
    ExecutorType(ExecutorType&& other) noexcept;
    ExecutorType(ExecutorType const& other) = delete;
    ExecutorType& operator=(ExecutorType&& other) noexcept = delete;
    ExecutorType& operator=(ExecutorType const& other) = delete;
 
    // Pour boost::asio::dispatch
    const PriorityScheduler& context() const noexcept;
 
    void add(std::function<void()> f);
 
   private:
    struct ExecutorTypeImpl;
    std::unique_ptr<ExecutorTypeImpl> impl_;
  };
 
  PriorityScheduler();
  ~PriorityScheduler();
 
  PriorityScheduler(PriorityScheduler&& other) noexcept = delete;
  PriorityScheduler(PriorityScheduler const& other) = delete;
  PriorityScheduler& operator=(PriorityScheduler&& other) noexcept = delete;
  PriorityScheduler& operator=(PriorityScheduler const& other) = delete;
 
  ExecutorType get_executor(int pri) noexcept;
 
  void run();
 
  void stop();
 
 private:
  struct PrioritySchedulerImpl;
  std::unique_ptr<PrioritySchedulerImpl> impl_;
};
 
bool operator==(const PriorityScheduler::ExecutorType& a,
                const PriorityScheduler::ExecutorType& b) noexcept;
 
bool operator!=(const PriorityScheduler::ExecutorType& a,
                const PriorityScheduler::ExecutorType& b) noexcept;
cpp/boost/executor/executor.cpp
#include "executor.h"
 
#include <algorithm>
#include <atomic>
#include <boost/asio/dispatch.hpp>
#include <boost/asio/execution_context.hpp>
#include <chrono>
#include <condition_variable>
#include <mutex>
#include <queue>
#include <utility>
#include <vector>
 
struct PriorityScheduler::PrioritySchedulerImpl final
    : public boost::asio::execution_context
{
  void run()
  {
    std::unique_lock<std::mutex> lock(mutex_);
    for (;;)
    {
      condition_.wait(lock, [&] { return stopped_ || !queue_.empty(); });
      if (stopped_)
      {
        return;
      }
      std::shared_ptr<item_base> p(queue_.top());
      queue_.pop();
      lock.unlock();
      p->execute_(p);
      lock.lock();
    }
  }
 
  void stop()
  {
    stopped_ = true;
    condition_.notify_all();
  }
 
  struct item_base
  {
    // Priorité de l'ordre.
    int priority_ = 0;
    // Pour trier les item ayant le même ordre.
    std::chrono::high_resolution_clock::time_point timestamp;
    // Fonction a exécuter dans un wrapper compatible avec
    // boost::asio::dispatch.
    std::function<void(std::shared_ptr<item_base>&)> execute_;
  };
 
  template <class Func>
  struct item final : item_base
  {
    item(int pri, Func&& f) : function_(std::forward<Func>(f))
    {
      priority_ = pri;
      execute_ = [](std::shared_ptr<item_base>& p) {
        Func tmp(std::move(static_cast<item*>(p.get())->function_));
        p.reset();
        tmp();
      };
      timestamp = std::chrono::high_resolution_clock::now();
    }
 
   private:
    Func function_;
  };
 
  // Pour comparer les items et savoir quel sera la prochaine fonction à
  // exécuter.
  struct item_comp
  {
    bool operator()(const std::shared_ptr<item_base>& a,
                    const std::shared_ptr<item_base>& b) const
    {
      if (a->priority_ != b->priority_)
      {
        return a->priority_ < b->priority_;
      }
      return a->timestamp > b->timestamp;
    }
  };
 
  std::mutex mutex_;
  std::condition_variable condition_;
  std::priority_queue<std::shared_ptr<item_base>,
                      std::vector<std::shared_ptr<item_base>>, item_comp>
      queue_;
 
 private:
  std::atomic<bool> stopped_{false};
};
 
struct PriorityScheduler::ExecutorType::ExecutorTypeImpl
{
 public:
  ExecutorTypeImpl(PriorityScheduler* ctx, int pri) noexcept
      : context_(ctx), priority_(pri)
  {
  }
  ~ExecutorTypeImpl() = default;
 
  ExecutorTypeImpl(ExecutorTypeImpl&& other) noexcept = delete;
  ExecutorTypeImpl(ExecutorTypeImpl const& other) = default;
  ExecutorTypeImpl& operator=(ExecutorTypeImpl&& other) noexcept = delete;
  ExecutorTypeImpl& operator=(ExecutorTypeImpl const& other) = delete;
 
  // Pour boost::asio::dispatch
  const PriorityScheduler& context() const noexcept { return *context_; }
  static void on_work_started() {}
  static void on_work_finished() {}
 
  // Partout Func est boost::asio::detail::work_dispatcher<void()>
  template <class Func, class Alloc>
  void dispatch(Func&& f, const Alloc& a) const
  {
    post(std::forward<Func>(f), a);
  }
 
 private:
  PriorityScheduler* context_;
  int priority_;
 
  template <class Func, class Alloc>
  void post(Func&& f, const Alloc& a) const
  {
    // On se force à utiliser l'allocateur fournit.
    std::shared_ptr<PriorityScheduler::PrioritySchedulerImpl::item_base> p(
        std::allocate_shared<
            PriorityScheduler::PrioritySchedulerImpl::item<Func>>(
            typename std::allocator_traits<Alloc>::template rebind_alloc<char>(
                a),
            priority_, std::forward<Func>(f)));
    // On sécurise seulement queue_.
    {
      std::lock_guard<std::mutex> lock(context_->impl_->mutex_);
      context_->impl_->queue_.push(p);
    }
    context_->impl_->condition_.notify_one();
  }
 
  // Pour boost::asio::dispatch
  template <class Func, class Alloc>
  void defer(Func&& f, const Alloc& a) const
  {
    post(std::forward<Func>(f), a);
  }
};
 
PriorityScheduler::ExecutorType::ExecutorType(PriorityScheduler* ctx, int pri)
    : impl_(std::make_unique<ExecutorTypeImpl>(ctx, pri))
{
}
 
PriorityScheduler::ExecutorType::~ExecutorType() = default;
 
PriorityScheduler::ExecutorType::ExecutorType(ExecutorType&& other) noexcept =
    default;
 
const PriorityScheduler& PriorityScheduler::ExecutorType::context()
    const noexcept
{
  return impl_->context();
}
 
void PriorityScheduler::ExecutorType::add(std::function<void()> f)
{
  boost::asio::dispatch(*impl_, f, nullptr);
}
 
bool operator==(const PriorityScheduler::ExecutorType& a,
                const PriorityScheduler::ExecutorType& b) noexcept
{
  return &a.context() == &b.context();
}
 
bool operator!=(const PriorityScheduler::ExecutorType& a,
                const PriorityScheduler::ExecutorType& b) noexcept
{
  return &a.context() != &b.context();
}
 
PriorityScheduler::PriorityScheduler()
    : impl_(std::make_unique<PrioritySchedulerImpl>())
{
}
 
PriorityScheduler::~PriorityScheduler() = default;
 
PriorityScheduler::ExecutorType PriorityScheduler::get_executor(
    int pri) noexcept
{
  return PriorityScheduler::ExecutorType(this, pri);
}
 
void PriorityScheduler::run() { impl_->run(); }
 
void PriorityScheduler::stop() { impl_->stop(); }
cpp/boost/executor/main.cpp
#include <iostream>
#include <thread>
 
#include "executor.h"
 
int main()
{
  PriorityScheduler sched;
 
  PriorityScheduler::ExecutorType&& nulle = sched.get_executor(-1);
  PriorityScheduler::ExecutorType&& low = sched.get_executor(0);
  PriorityScheduler::ExecutorType&& med = sched.get_executor(1);
  PriorityScheduler::ExecutorType&& high = sched.get_executor(2);
 
  med.add([] { std::cout << "2\n"; });
  low.add([] { std::cout << "1\n"; });
  med.add([] { std::cout << "22\n"; });
  low.add([] { std::cout << "11\n"; });
  high.add([] { std::cout << "3\n"; });
  high.add([] { std::cout << "33\n"; });
  high.add([] { std::cout << "333\n"; });
 
  nulle.add([&sched] { sched.stop(); });
 
  std::thread t([&sched] { sched.run(); });
 
  t.join();
}

boost::algorithm

boost::algorithm::join et boost::adaptors::transformed

#include <iostream>
 
#include <boost/algorithm/string/join.hpp>
#include <boost/range/adaptor/transformed.hpp>
 
#include <string>
 
struct Data
{
  std::string name;
  std::string version;
};
 
int main()
{
  std::vector<Data> v{ {"Name1", "Version 1"}, {"Name2", "Version 2"} };
 
  std::cout <<
  boost::algorithm::join(
    v |
    boost::adaptors::transformed(
      [](const Data& firmware)
      {
        return firmware.name + " / " + firmware.version;
      }
    ),
    ", ") <<
  std::endl;
 
  return 0;
}

Migrer de boost vers std

Pointeur intelligent

Aucune différence entre :

  • std::shared_ptr et boost::shared_ptr,
  • std::unique_ptr et boost::unique_ptr,
  • std::weak_ptr et boost::weak_ptr,
  • std::make_shared et boost::make_shared,
  • std::enable_shared_from_this et boost::enable_shared_from_this.

shared_ptr, make_shared et enable_shared_from_this doivent tous venir de la même libraire. L'un en version std et l'autre en version boost sont incompatibles.

  • boost::ref et std::ref.

assign

Utiliser list_initializer.

//const std::list<int> primes = boost::assign::list_of(2)(3)(5)(7)(11);
const std::list<int> primes {2, 3, 5, 7, 11};

foreach

short array_short[] = { 1, 2, 3 };
BOOST_FOREACH( short & i, array_short )
  ++i;
short array_short[] = { 1, 2, 3 };
for (short & i : array_short)
  ++i;

Et avec des containers multi valeurs :

std::map<const int, int> m;
int key, value;
BOOST_FOREACH(boost::tie(key, value), m)
{
  // do something with key and value here
}
std::map<int, int> m;
int key, value;
// Ici, key sera en lecture seule (implicite à la structure de std::map) et value sera accessible en écriture.
// En cas de présente / absence explicite de const, cela s'applique obligatoirement à toutes les variables.
for(auto & [key, value] : m)
{
  // do something with key and value here
}

optional

Il ne devrait pas y avoir de problèmes. Il faut noter que std ne supporte pas les références : std::optional<int&>.

boost::none devient std::nullopt.

regex

Cette fonctionnalité était très peu utilisée.

boost::regex_search(name, whatpath, expmatchIndexPath, boost::match_default);
// std::regex_search ne supporte pas le 4ème paramètre.
std::regex_search(name, whatpath, expmatchIndexPath);

Sinon, boost::cmatch et boost::regex sont identiques à std::cmatch et std::regex.

variant

boost::variant<int, double> v = 5;
double d = boost::get<double>(v); // Génère une exception
double *d = boost::get<double>(&v); // Renvoi nullptr

devient

std::variant<int, double> v = 5;
double d = std::get<double>(v); // Génère une exception
double *d = std::get_if<double>(&v); // Renvoi nullptr

boost::apply_visitor devient std::visit.

Attention, boost::variant<int, int> est accepté mais pas std::variant<int, int>. Dans tous les cas, il ne faut pas y avoir deux fois le même type.

tuple

Non testé la migration.

bind

Les deux sont compatibles. Mais il est préférable de les remplacer par des fonctions lambda.

filesystem

Dans les cas basiques que j'ai eu, je n'ai pas eu de différences. Mais il y a quelques différences apparemment. Archive du 06/05/2019 le 03/03/2020

  • boost::filesystem::ifstream
#include <boost/filesystem/fstream.hpp>
 
boost::filesystem::path currentPath;
boost::filesystem::ifstream ifs(nextPath);

devient

#include <filesystem>
#include <fstream>
 
std::filesystem::path currentPath;
std::ifstream ifs(nextPath.c_str());
  • boost::filesystem::copy_option

Il devient std::filesystem::copy_options. Le nom des enum est différent :

overwrite_if_exists devient overwrite_existing. fail_if_exists devient skip_existing.

  • Deprecated

leaf() devient filename().

Deprecated names and features Archive du 29/12/2014 le 21/04/2020

thread

  • thread.join : si le thread n'est pas joinable(), boost ne fait rien mais std génère une exception.

shared_mutex

Il n'y a pas d'équivalent car std ne supporte pas le timeout.

Il faut alors faire une boucle où on passe son temps à faire un try_lock_shared pendant le temps nécessaire.

scoped_array

Peut se remplacer sans problème par un std::vector.

algorithm

  • string
std::string str;
return boost::to_upper_copy(str);

devient

std::string str;
std::transform(str.begin(), str.end(), str.begin(), ::toupper);
return std::move(str);

boost::lexical_cast

boost::lexical_cast<std::string>(number) devient std::to_string(number).

lib/boost.txt · Dernière modification : 2020/07/05 00:37 de root