יום ראשון, אפריל 05, 2015

חוויות בתהליך מעבר ל CMAKE + c++11 שלום עולם

התמזל מזלי ומתנה נפלה בחלקי העברת קוד שמשתמש במגוון ספריות לשימוש ב C++11 יחסית ביחד עם CMake.

התהליך התעכב רבות והתחיל רק השבוע פה לכן אני חושב לרכז מספר הגיגים על התהליך.

אני מקווה לטוב ולכן נכון לעכשיו אני בודק רק על gcc אבל אני מקווה שהכל ילך חלק וזה גם יעבוד על VS 2013 איתו אני עובד יותר.

CMakeLists עבור שלום עולם יראה כך :

cmake_minimum_required(VERSION 2.6)
project(hello_world)
FIND_PACKAGE (Threads)


if (CMAKE_COMPILER_IS_GNUCXX)
  SET(CMAKE_CXX_FLAGS "-std=gnu++11") # for C++11 in gcc
endif()

add_executable(hello_world main.cpp)
target_link_libraries (hello_world ${CMAKE_THREAD_LIBS_INIT})


install(TARGETS hello_world RUNTIME DESTINATION bin)

הוספת המשתנה CMAKE_CXX_FLAGS פותרת את:


In file included from /usr/include/c++/4.9/thread:35:0,
                 from /opt/video/wasteoftime/cpp/threading/proj/hello_world/main.cpp:2:
/usr/include/c++/4.9/bits/c++0x_warning.h:32:2: error: #error This file requires compiler and library support for the ISO C++ 2011 standard. This support is currently experimental, and must be enabled with the -std=c++11 or -std=gnu++11 compiler options.
 #error This file requires compiler and library support for the \
  ^ 


השימוש ב ${CMAKE_THREAD_LIBS_INIT} דרוש בשביל שניתן יהיה להשתמש בנימים , ועל הדרך פותר החריגה (שאין לי שמץ למה היא קוראת)
terminate called after throwing an instance of 'std::system_error'
  what():  Unknown error -1

כאשר משתמשים ב std::call_once (אני לא יודע אם זה באג או שאני פשוט מפספס משהוא בסיסי) ולא מתלנקקים מול pthread דוגמאת קוד לריסוק:

#include <iostream>
#include <mutex>

class resource
{
  std::once_flag resource_flag;
  void init_once(){}
public:
  resource(){};
  ~resource(){};
  void  doSomething()
  {
    std::call_once(resource_flag,&resource::init_once,this);
    return;
  }
};

int main()
{
  resource r;
  r.doSomething();
  return 0;
}

ככלל קוד שלום עולם נראה כך :

#include <iostream>
#include <thread>

static void f()
{
    std::cout << "Hello, world!" << std::endl;
}

int main(int argc, char **argv) {
  
    std::thread t (f);
    t.join();
    return 0;
} 
הפתעה כואבת היתה (כן כן אני יודע באג של מתחילים) כאשר העברתי ייחוס (reference) לאובייקט ארעי במקום לאובייקט עצמו (קוד שיעורי בית עם באג) :

#include <iostream>
#include <thread>
#include <vector>
#include <mutex>
int accum = 0;

void square (int x , std::mutex & mutex) 
{
  std::lock_guard <std::mutex> guard( mutex);
  accum += x * x;
}

int main()
{
  std::vector < std::thread> threads;
  std::mutex mutex;
  for (int i = 1; i < 21 ; i ++ )
  {
     threads.push_back(std::thread (  square, i,mutex)) ; 
  }
  for (std::vector < std::thread>::iterator it = threads.begin();it != threads.end();++it)
  {
    it->join();
  }
  std::cout << accum << std::endl;
  return 0;
}

הנקודה הבעייתית במקרה של העברת ייחוס לאובייקט אירעי ולא לאובייקט עצמו:

     threads.push_back(std::thread (  square, i,mutex)) ; 
הקוד הנכון הוא :
     threads.push_back(std::thread (  square, i,std::ref (mutex) )) ; 

הופתעתי לטובה כי ניתן להשתמש די בקלות בהעברת חריגות כמעת בצורה זהה לבוסט קוד (קוד דוגמא) בבוסט :

#include <boost/exception/all.hpp>
#include <boost/thread.hpp>
#include <boost/bind.hpp>
#include <iostream>

void avodashora()
{
  if (  rand () % 100 == 0)
  {
    throw boost::enable_current_exception(std::runtime_error("piturim"));
  }
}

class oved_cablan
{
public:
  void operator()(boost::exception_ptr & tzarot)  
  {
    try 
    {
      while (true)
      {
        avodashora();
      } 
    }
    catch (...)
    {
      tzarot = boost::current_exception();
    }
  }
};

class dor_aleph
{
public:
    dor_aleph(): 
    ben_dod_shel
    (
      boost::bind 
      (
        &oved_cablan::operator(),
        boost::ref(oved_pashut),
        boost::ref( tzarot ) 
      ) 
    )
    {
      //a dor_aleph , needs ben_dod_shel to the work for him
      //any problem that ben_dod_shel will get be refernced to oved_pashut
    }

    void work()
    {
      ben_dod_shel.join();//our dor aleph need to wait till ha ben dod will finish the work he assigned to

      if (tzarot)
      {
        boost::rethrow_exception( tzarot );
      }
    }
private:
    oved_cablan          oved_pashut;
    boost::thread        ben_dod_shel;
    boost::exception_ptr tzarot;
};

int main()
{
  try
  {
   dor_aleph a;
   a.work();
  }
  catch (std::exception & e)
  {
    std::cout << e.what() << std::endl;
  }
  return 0;
}
הקוד המומר לc++11

#include <boost/exception/all.hpp>
#include <iostream>
#include <thread>

void avodashora()
{
  if (  rand () % 100 == 0)
  {
    throw std::runtime_error("piturim");
  }
}

class oved_cablan
{
public:
  void operator()(std::exception_ptr & tzarot)  
  {
    try 
    {
      while (true)
      {
        avodashora();
      } 
    }
    catch (...)
    {
      tzarot = std::current_exception();
    }
  }
};

class dor_aleph
{
public:
    dor_aleph(): 
    ben_dod_shel(oved_pashut,std::ref(tzarot))
    {
      //a dor_aleph , needs ben_dod_shel to the work for him
      //any problem that ben_dod_shel will get be refernced to oved_pashut
    }

    void work()
    {
      ben_dod_shel.join();//our dor aleph need to wait till ha ben dod will finish the work he assigned to

      if (tzarot)
      {
        std::rethrow_exception( tzarot );
      }
    }
private:
    oved_cablan        oved_pashut;
    std::thread        ben_dod_shel;
    std::exception_ptr tzarot;
};

int main()
{
  try
  {
   dor_aleph a;
   a.work();
  }
  catch (std::exception & e)
  {
    std::cout << e.what() << std::endl;
  }
  return 0;
}

אין תגובות:

הוסף רשומת תגובה