29 Ocak 2018 Pazartesi

variant Sınıfı

Variant Sınıfı
Giriş
Şu satırı dahil ederiz.
#include <boost/variant.hpp> 
Açıklaması şöyle
You can think about boost::variant as a improved C++ union.
Union'daki her tanım farklı bir tip olduğu için hep aynı tipi içeren şöyle bir variant mantıklı değil.
boost::variant<T, T>
İleride std::variant sınıfı da standarda girebilir. boost::variant hem farklı tipleri aynı değişkende saklamak, hem de "template covariance" için kullanılır.

Parametre Sayısı
Sayı 20 ile sınırlı. Eğer değiştirmek istersek BOOST_MPL_LIMIT_LIST_SIZE ile değiştirilebilir. Bu sayının 10'un katı olması gerekir. Yani 30,40,50 olabilir. Üst sınıf 50'dir. Şöyle yaparız.
#define BOOST_MPL_CFG_NO_PREPROCESSED_HEADERS
#define BOOST_MPL_LIMIT_LIST_SIZE 30

Kötü Örnekler
Örnek
Variant static polymorphisim için kullanılır. Dolayısıyla birbirlerinden kalıtan tipler için kullanmak mantıklı değil. Kötü bir variant örneği için şöyle yaparız.
using Shape = boost::variant<Rectangle, Circle>;
Örnek
variant sınıfı içerdiği tiplerin alanlarına direkt erişim sağlamaz. Açıklaması şöyle
The variant type doesn't have the .hello and .bye members. You can access them via a "visitor" function.
Şu kod derlenmez.
class A {
public:
  struct greeting {
    string hello;
  };
};

class B {
public:
  struct greeting {
    string bye;
  };
};

typedef boost::variant<A::greeting, B::greeting> greet;

greet getG(string key) {
  greet g;
  if (key == "A") {
    g.hello = "MY ENEMY"; // this line doesn't work
  }
  else {
    g.bye = "MY FRIEND"; // nor this line
  }
  return g;
};
Constructor
Tanımlanan tipin CopyConstructible ve MoveConstructible olması gerekir. Açıklaması şöyle
Every bounded type must fulfill the requirements of the MoveAssignable concept.
Şöyle yaparız.
struct empty_type {};
using cell_type = boost::variant<std::string, double, empty_type>;
Ya da şöyle yaparız.
typedef boost::variant<std::string,int> StringOrInt;
Daha sonra variant nesnesine bir değer atarız. Şöyle yaparız.
StringOrInt v = 0;
veya şöyle yaparız.
StringOrInt v= "42";
Eğer tip move assignment metoduna sahip değilse hata alırız. Move constructor tanımlanınca, move assignment derleyici tarafından üretilmediği için derlenmez.
#include <boost/variant.hpp>

struct Foo {
    Foo(Foo&&) { }
};

int main() {
    boost::variant<int, Foo> v;
    v = 42;
}
get metodu
Bu metod aslında variant sınıfına ait değil ancak buraya yazmayı daha uygun buldum. Variant içindeki değere referans ile erişmek için boost::get(...) kullanılabilir. Eğer boost::get(...) istenilen tipe dönüştüremiyorsa exception fırlatır.

std::variant ile boost::variant'ın ayrıldıkları bir nokta var. std::get ile indeks kullanarak istenilen tipe dönüşüm sağlanabilir. Şöyle yaparız.
std::get<0>(v);
boost::get() bu yeteneği sağlamıyor ancak bir miktar kod ile kolayca çözülebilir.

Örnek
Elimizde şöyle bir variant olsun.
boost::variant<User*, Stuff*> v;
Şöyle yaparız.
User **user = boost::get<User*>(&v);
type metodu
Bu metodu kullanarak if koşulları kodlamak yerine boost::static_visitor kullanmak daha iyi olabilir.
Örnek
Şöyle yaparız.
if(myVariantInstance.type() == typeid(ConcreteType))
{...}
Örnek
Elimizde iki tip içeren bir variant olsun Şöyle yaparız.
boost::variant<Rectangle, Triangle> v = ...;

if (v.type() == typeid(Rectangle)) {
 ...
} else if (v.type() == typeid(Triangle)){
  ...
}
which metodu
Variant içindeki değere referans ile erişmek için boost::get + variant.which() kullanılabilir. Açıklaması şöyle
Such a getter already exists: boost::get<> function template.

However, keep in mind that the variant contents is set at run-time, so it is absolutely impossible to know it at compile-time.

That's why you should tell get function template what type you want it to return - and if the variant does not contain it at that point of time, it will throw exception.

Alternatively, you could use variant::which() member function, which returns an index of the current type - which is also a run-time value.
Elimizde şöyle bir variant olsun
using VariantType = boost::variant<
    std::shared_ptr<StructA>, 
    std::shared_ptr<StructB>
>;
Variant'i şöyle ilkendireyim
VariantType variant = std::make_shared<StructA>(1, 'a', 3);
which metodunu çağırayım.
std::cout << variant.which() << std::endl;
İlk tipi kullandığım için çıktı olarak 0 alırım.
0
Variant'i bu sefer şöyle ilkendireyim
VariantType variant = std::make_shared<StructB>('b', 'c');
which metodunu çağırayım.
std::cout << variant.which() << std::endl;
İkinci tipi kullandığım için çıktı olarak 1 alırım.
1

Diğer
Variant'ı Parametre Olarak Geçme
Şöyle yaparız.
template <typename O, typename E, template <typename...> class VariantType>
VariantType<O, E> foo (O o)
{
  return VariantType<O, E>{std::move(o)};
}

boost::variant<int, std::string> res = foo <int, std::string, boost::variant>(17);
Variant Listesi 
Şöyle yaparız.
using V = boost::variant< int, std::string>;
for (V v : { V{5}, V{"something"} })
  boost::apply_visitor(myvisitor(), v);
Variant Vector'ü
Şöyle yaparız.
vector<StringOrInt> values; 
values.push_back (0);
values.push_back ("42");
Yazdırmak için şöyle yaparız.
for (int i = values.size()-1; i > 0; --i)
  std::cout << values[i] ;
Variant Map'i
Örnek 1
Tanımlamak için şöyle yaparız.
std::map<std::string,boost::variant< int*, std::string*>> options;
Şöyle yaparız.
std::string key = ...;
int value = ...;
*boost::get<int*>(options.find(name)->second) = value;
Örnek 2
Şöyle yaparız.
using Key = boost::variant<int, std::string>;
using Map = std::map<Key, std::string>;

Map m;

m[1] = "aaa";
m["myKey"] = "bbb";
recursive_wrapper
recursive_wrapper yazısına taşıdım

Hiç yorum yok:

Yorum Gönder