pretty

Friday 13 November 2015

How to link pre-built library with CMake


1. Provide path to library (for example, default path may be set to usr/local/lib on Linux, and the lib won't be found even when located in the same directory as CMakeLists.txt and even with full path provided inside target_link_libraries). Suppose library is in the subfolder /lib of the folder where CMakeLists.txt resides, then:
set(PATH_TO_LIB ${CMAKE_CURRENT_LIST_DIR}/lib/)
find_library(MyLibVariable NAMES "MyLibrary" PATHS ${PATH_TO_LIB} NO_DEFAULT_PATH)
NAMES should provide the name of library, without the lib prefix. So if there is libMyLibrary.so it shoud read "MyLibrary". NO_DEFAULT_PATH removes all default paths from search. CMAKE_CURRENT_LIST_DIR is the directory where CMakeLists.txt is located.

2. Use target_link_libraries on the found library, presented by MyLibVariable
target_link_libraries(${APP_NAME} 
    ${MyLibVariable}
)
APP_NAME is the name of application to link the library to, added with add_executable(${APP_NAME} ${SRC} ${HEADERS}) in this CMakeLists.txt
In case the library still cannot be located you can debug the path is really the one you suppose it should be, including in CMakeLists.txt the following:
MESSAGE('--- MyLibVariable ---')
MESSAGE(${MyLibVariable})
In case path is still wrong make sure to delete the CMakeCache.txt, as it might have cached previous value.

Monday 28 September 2015

Morphing animation for Toolbar back button and Navigation drawer icon (Android)


It is common to use one of the third-party libs to create a morphing animation for icon, I have used the popular material-menu library. With this libray it is easy to use to morph animation for the navigation drawer icon, or any other icon in ui. However, the problem came up when I wanted to animate the transition from the Navigation drawer burger icon to Toolbar back button. The scenario is as follows. There is a Navigation drawer icon on the left side of Toolbar, also there is a collapsed SearchView in Toolbar. When the user clicks on search icon on the left side of Actionbar, SearchView expands and the burger icons turns into the back icon. Pressing this back button hides SearchView. The desired effect is to morph the back button into the hamburger icon and vice versa.

This is easier said then done because the burger button and back button are actually two different buttons. But, looking into Android source for android.support.v7.widget.Toolbar it is obvious that the two buttons are actually mNavButtonView and mCollapseButtonView, and they luckily have the same layout parameters and positioning (see ensureNavButtonView() and ensureCollapseButtonView() methods).

Now, the basic idea is to setup the same morphing drawable on both buttons, and play animation on them simultaneously - and hence animation will always be seen irrespective of the current visible button. For the Navigation drawer button drawable can be set simply with the call to setHomeAsUpIndicator(). To set the back button drawable we must use reflection to get to the mCollapseButtonView field and ensureCollapseButtonView() method. We need to call the ensureCollapseButtonView() method first to make sure that the mCollapseButtonView is created.


import com.balysv.materialmenu.MaterialMenuDrawable;

...


private MaterialMenuDrawable mToolbarMorphDrawable;
private MaterialMenuDrawable mSearchViewMorphDrawable;

... 

private void setupActionBar(final Toolbar toolbar) {
  
mToolbarMorphDrawable = new MaterialMenuDrawable(this, Color.BLACK,
                                           MaterialMenuDrawable.Stroke.THIN);
    
mToolbarMorphDrawable.setIconState(MaterialMenuDrawable.IconState.BURGER);
 
mSearchViewMorphDrawable = new MaterialMenuDrawable(this, Color.BLACK, 
                                           MaterialMenuDrawable.Stroke.THIN);

mSearchViewMorphDrawable.setIconState(MaterialMenuDrawable.IconState.BURGER);

Toolbar toolbar = setupActionBar((Toolbar) findViewById(R.id.upperToolbar));
setSupportActionBar(toolbar);
final ActionBar ab = getSupportActionBar();
if (ab != null) {
    ab.setHomeAsUpIndicator(mToolbarMorphDrawable);
    ab.setDisplayHomeAsUpEnabled(true);
}

try {
    Method ensureCollapseButtonView = android.support.v7.widget.Toolbar.class
                        .getDeclaredMethod("ensureCollapseButtonView", null);

    ensureCollapseButtonView.setAccessible(true);
    ensureCollapseButtonView.invoke(toolbar, null);

    Field collapseButtonViewField = android.support.v7.widget.Toolbar.class.
                                    getDeclaredField("mCollapseButtonView");

    collapseButtonViewField.setAccessible(true);

    ImageButton imageButtonCollapse = (ImageButton) collapseButtonViewField
                                                             .get(toolbar);

    imageButtonCollapse.setImageDrawable(mSearchViewMorphDrawable);
}
catch (Exception e) {
    // Something went wrong, let the app work without morphing the buttons :)
    e.printStackTrace(); 
}
}


That is the OnActionExpandListener that starts morphing animation when the expanded action view (SearchView in our case) expands and collapses.

MenuItemCompat.setOnActionExpandListener(searchMenuItem, 
                            new MenuItemCompat.OnActionExpandListener() {
            @Override
            public boolean onMenuItemActionCollapse(MenuItem item) {
                mToolbarMorphDrawable.animateIconState(MaterialMenuDrawable
                                                         .IconState.BURGER);
                mSearchViewMorphDrawable.animateIconState(MaterialMenuDrawable
                                                         .IconState.BURGER);
                return true;
            }

            @Override
            public boolean onMenuItemActionExpand(MenuItem item) {
                mToolbarMorphDrawable.animateIconState(MaterialMenuDrawable
                                                          .IconState.ARROW);
                mSearchViewMorphDrawable.animateIconState(MaterialMenuDrawable
                                                          .IconState.ARROW);
                return true;
            }
        });


You might ask, why there are two separate drawables for these two buttons. Yes, we might have designated one drawable instance but then the animation wouldn't work as expected because one of the buttons "locks" the drawable and this makes the animation to freeze when another button becomes invisible.

Note that the reflection is used on the android.support.v7.widget.Toolbar.class, but if you use android.widget.Toolbar then change this line. Other than that android.widget.Toolbar seems to have the same fields and methods names so that you can try the same technique for it.

Wednesday 24 June 2015

Using javah to generate jni header for Android class


1. Open the directory containing the root of android package. For example, if android class is located in SampleActivity.java, and it's package is com.sample.android, then open the folder containing the /com folder (in my case it is located in android_project/app/src/main/java/).

2. From this folder isssue the command javah com.sample.android.SampleActivity.

Corresponding .h file should be generated in-place in this folder. You can move it to another folder.

Wednesday 17 June 2015

C++ 11 when do detached threads termiante?


What is the fate of the thread that is detached via std::thread::detach()? In the common situation the detached thread would be abandoned when the std::exit() would be called, and std::exit() is called on return from main() whether there are additional threads in the process or not.
#include "stdlib.h"
#include "stdio.h"
#include "unistd.h"
#include "string"
#include "pthread.h"

class Global {
public:
    ~Global()
    {
        std::cout << "Global object dtor\n";
    }
};

class A {
  public:
  ~A()
  {
     std::cout << "A dtor\n";
  }
};

void thread_main()
{
  int i = 0;
  A a;
  while (true) {
   std::cout << "thread_main" << i++ << std::endl;
   sleep(1);

   if (i == 5) break;
  }
}

void atexit_handler()
{
    std::cout << "atexit handler\n";
}

Global global_variable; 

int main(int argc, char **argv)
{
  const int result = std::atexit(atexit_handler);
  std::thread t(thread_main);
  t.detach();
  return 0;
}
This snippet would produce the following output, note that the destructor for the object allocated on detached thread is not getting called.
atexit handler
thread_main0
Global object dtor
*** Exited normally ***
So the thread is abandoned non-gracefully, in a manner that reminds of the daemon thread in Java. Really, while JVM would not finish application if there is at least one active non-daemon thread, but if the thread is daemon thread it's existence wouldn't prevent app from exiting, and such thread would be abandoned - no stack unwinding, no finally blocks getting called for it. Such threads are used for tasks like garbage collection.

What if std::exit() would not be called from main()? In the following snippet I'm finishing the main thread without calling return and hence exit() is not getting called, because there's still a running thread around.
int main(int argc, char **argv)
{
  const int result = std::atexit(atexit_handler);
  std::thread t(thread_main);
  t.detach();
  pthread_exit(NULL);
  //return 0;
}
This will let the detached thread complete and then the process exits releasing all the resources.
thread_main0
thread_main1
thread_main2
thread_main3
thread_main4
A dtor
atexit handler
Global object dtor
*** Exited normally ***

Thursday 11 June 2015

C++11 async launch::async vs launch::deferred


With launch::async flag the foo() function would be executed in a separate spawned thread immediately.
On the other hand with launch::deferred foo() will be executed on the same thread, and at the moment when future.wait() or future.get() would be called.
void  foo () {
    std::cout << "foo threadid = " << std::this_thread::get_id() << '\n';
}

int main()
{
    std::cout << "my threadid = " << std::this_thread::get_id() << '\n';
    std::future fut = std::async(std::launch::deferred, foo);
    fut.get();
    return 0;
}
my threadid =  140737353906048
foo threadid = 140737353906048

Friday 22 May 2015

std::move overload resolution


#include 
class A
{
public:
  A(){}
  A(A& other) { std::cout << "copy ctor" << std::endl; }
  A(const A& other) { std::cout << "const copy ctor" << std::endl; }
  A(A&& other) { std::cout << "move ctor" << std::endl; }
};

class B
{
public:
  B(){}
  B(B& other) { std::cout << "copy ctor" << std::endl; }
  B(const B& other) { std::cout << "const copy ctor" << std::endl; }
};

class C
{
public:
  C(){}
  C(C& other) { std::cout << "copy ctor" << std::endl; }
};

int main()
{
  A a1;
  A a2(std::move(a1)); // move ctor called
  
  B b1;
  B b2(std::move(b1)); // const copy ctor called
  
  C c1;
  // C c2(std::move(c1)); // compile-time error: no matching constructor for initialization of 'C'

  return 0;
}
    C c1;
    const C &c2 = std::move(c1); // OK: const reference can bind to rvalue
    C && c3 = std::move(c1); // OK: rvalue reference
    // C &c4 = std::move(c1); // Error: Non-const reference cannot bind to rvalue
    // C c5 = std::move(c1); // Error: No move constructor or const copy constructor in C
Quote: "Rvalue references can be used to extend the lifetime of a modifiable temporary (note, lvalue references to const can extend lifetimes too, but they are not modifiable)"

Setup clang compiler in Kdevelop


1. Install clang compiler with "sudo apt-get install clang-3.5"
2. In CMakeLists.txt of the project add at the very top of the file (before any other tags, like cmake_minimum_required() or project())
set(CMAKE_C_COMPILER "/usr/bin/clang-3.5")
set(CMAKE_CXX_COMPILER "/usr/bin/clang++-3.5")
3. In Kdevelop right-click on the project->Open Configuration->click on CMake tab->Click on a green plus button to configure new build directory. Put in a name for the new folder, like /home/d/projects/SomeProject/build_clang. Click "Apply" button. The Build console should print something like:
-- The C compiler identification is Clang 3.5.0
-- The CXX compiler identification is Clang 3.5.0
-- Check for working C compiler: /usr/bin/clang-3.5
-- Check for working C compiler: /usr/bin/clang-3.5 -- works
-- Detecting C compiler ABI info
-- Detecting C compiler ABI info - done
-- Detecting C compile features
-- Detecting C compile features - done
-- Check for working CXX compiler: /usr/bin/clang++-3.5
-- Check for working CXX compiler: /usr/bin/clang++-3.5 -- works
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Detecting CXX compile features
-- Detecting CXX compile features - done
...