19 Aralık 2018 Çarşamba

Windows'ta DLL Geliştirmek

Giriş
DLL'den metod export etmek için
1. GetProcAddress metodu ile yüklenen metodların bulunması ya
2..def dosyasında tanımlı olması ya da
3.şu şekilde tanımlı
extern "C" __declspec(dllexport) ...
olarak tanımlı olması gerekir. Bu yöntemi kullanıyorsak dll projesinin. lib çıktısı ile ana projeyi linkleriz.

1..def dosyası
.def dosyaları çok eski bir yöntem ve artık kullanılmamalı.

2. extern "C" __declspec(dllexport)
__declspec(dllexport) kelimelerinden sonra calling convention'ı belirlemek gerekir. Bu kelimeler metodun PE export tablosuna eklenmesini sağlar.

1.C calling convention'da (__cdecl) çağıran metod stack'i temizler. __cdecl Calling Convention yazısına bakabilirsiniz.

2. Windows calling convention'da (__stdcall) çağrılan metod stack'i temizler.

Örnek - Belirtilmemiş
Windows DLL metodları otomatik olarak "__stdcall convention"'ı uygular. Şöyle yaparız.
extern "C" __declspec(dllexport) void foo(void* v) { int a = 0; }
Daha sonra .lib ile linkelemediysek şöyle çağırırız.
foo f = (foo)GetProcAddress(hmod, "foo");
foo kendi içinde başka metodları kullanabilir.
extern "C" __declspec(dllexport) foo()
{
  int result = Bar();
}
Örnek
Şöyle yaparız.
extern "C" __declspec(dllexport) int validate( int v_mode, char * data, int data_length );
Örnek - __cdecl
Şöyle yaparız. Burada calling convention olarak __cdecl kullanılıyor.
extern "C" __declspec(dllexport) int __cdecl test();
Örnek - __stdcall
Şöyle yaparız.
#ifdef PROJET_EXPORTS
#  define EXPORT __declspec(dllexport)
#else
#  define EXPORT __declspec(dllimport)
#endif

#define CALLCONV_API __stdcall

#ifdef __cplusplus
extern "C" // C wrapper
{
#endif

    EXPORT void CALLCONV_API foo(void);
#ifdef __cplusplus
}
#endif
Örnek
Export edilen metod C çağrısı olduğu için imzasında C++ tipleri kullanılamaz. Açıklaması şöyle.
When you write an exported DLL function, you SHOULD NOT use any C++ library types in its interface, as these are compiler, version, and even solution (DEBUG or NDEBUG) dependent.

If you do, you must make sure that the caller uses the same implemetation of those types.

You should restrict DLL exported functions to use only types in their interface that are compatible with C types, or other mutually agreed upon types.

Inside your DLL implementation, you can do whatever you want!
Şu kod std::wstring kullandığı için yanlış. Başka bir derleyici bu DLL'i kullanamayabilir.
extern "C" __declspec(dllexport) void Msg(std::wstring filename)
{   
  ...
}
string
DLL'e unicode string parametre geçmek istersek LCWSTR/LPWSTR yani const wchar_t * kullanılabilir.

17 Aralık 2018 Pazartesi

std::piecewise_construct

Giriş
Tuple ile kullanılır. Tuple'ın birinci ve ikinci değerlerine erişebilmeyi sağlar.

Örnek
Şöyle yaparız.
std::map<int, non_copyable> m;
m.emplace(std::piecewise_construct, std::tuple(0), std::tuple());
m.emplace(std::piecewise_construct, std::tuple(1), std::tuple());

Most Vexing Parse

Giriş
Most Vexing Parse "en sinir bozucu parse" olarak çevirilebilir. Vexation ile ilgili.Bir satırın değişken tanımlaması mı yoksa fonksiyon tanımlaması mı olduğunun ayırt edilememesi anlamına geliyor.

Düzeltmek İçin
Şöyle yaparız. Constructor için süslü parantez veya geçici bir değişken kullanmak yeterli.
You can disambiguate this from a declaration by either using Boo{num}; instead of Boo(num); (because {} around the declarator is not allowed.), by making the temporary a named variable, e.g. Boo temp(num);, or by putting it as an operand in another expression, e.g. (Boo(num));, (void)Boo(num);, etc.

Hata mesajı
Visual Studio'da şuna benzer
warning C4930: '...': prototyped function not called (was a variable definition intended?)
Clang şuna benzer.
warning: parentheses were disambiguated as redundant parentheses around declaration of
variable named 'num' [-Wvexing-parse]
Boo(num); // No default constructor 
Default Constructor Zannedilmesi
Örnek
Şöyle yapmamalıyız. Default constructor'ın çağrıldığı düşünülüyor ancak aslında Circle nesnesi dönen c2 metodu tanımlanıyor.
Circle c2();
Örnek
Şöyle yapmamalıyız.
// does not compile
Foo (value_1);
Constructor'a Parametre Geçildiğinin Zannedilmesi
Örnek
Şöyle yapmamalıyız. Constructor'a parametre geçdildiği düşünülüyor ancak aslında Box parametresi alan ve Box nesnesi dönen a metodu tanımlanıyor.
Box a(Box())
Düzeltmek için şöyle yaparız.
Box a{Box{}}
Örnek
Şöyle yaparız.
#include <memory>

class Foo
{
public:
  Foo() {};
};

class Bar
{
public:
  Bar( const std::shared_ptr<Foo>& foo ) {}
}; 

int main()
{
  Foo* foo = new Foo;
  Bar bar( std::shared_ptr<Foo> {foo} ); //Derleme hatası almamak için
  return 0;
}
Üye Alan Tanımlandığının Zannedilmesi
Örnek
Elimizde şöyle bir kod olsun. a tipinden Test isimli nesne tanımlandığı zannediliyor ancak aslında Test isimli metod tanımlanıyor.
#include <iostream>
using namespace std ;

class A
{
  public:
    A(int xinit, int yinit){x=xinit; y=yinit;}

  private:
    int x, y;
};

class B
{
  public:
    b(int pinit, int qinit){p=pinit; q=qinit;}

  private:
    int p,q;
    a Test(p,q); //Most verxing parse hatası
};