C/C++ Birden fazla türde return

TheAny

Megapat
Katılım
18 Aralık 2018
Mesajlar
7.625
Makaleler
13
Çözümler
102
Merhaba;

Gorgon: Summary üzerinde map vb sistemler için bir şey geliştiriyorum. Gorgon'un içerisinde Struct.h kısmına baktığınızda reflection becerisini olduğunu göreceksiniz. Bu becerisinden yararlanıp setbyname adında bir fonksiyon oluşturdum. Şimdi aynı mantıkla getbyname oluşturmak istiyorum.

Aşağıda SetByName var.
C++:
// Using Gorgon's Reflection system to handle member assignments.
    namespace internal {
        template<class T_, int Ind>
        // Sets the member by name if the name exists in the structure's reflection.
        void SetByName_setif(T_ &obj, const std::string &name, const std::string &value) {
            if(T_::Reflection().Names[Ind] == name)
                obj.*(T_::ReflectionType::template Member<Ind>::MemberPointer()) = Gorgon::String::To<typename T_::ReflectionType::template Member<Ind>::Type>(value);
        }
        // Since we can't travel template members in run time, we use Gorgon Sequence for to do that using template parameters.
        template<class T_, int ...S_>
        void SetByName_expand(Gorgon::TMP::Sequence<S_...>, T_ &obj, const std::string &name, const std::string &value) {
            (SetByName_setif<T_, S_>(obj, name, value), ...);
        }
    }
    // Caller for the above.
    template<class T_>
    void SetByName(T_ &obj, const std::string &name, const std::string &value) {
        internal::SetByName_expand(typename Gorgon::TMP::Generate<T_::ReflectionType::MemberCount>::Type{}, obj, name, value);
    }

Aynı mantıktan yola çıkarak önce şunu yaptım;
C++:
template<class T_, int Ind>
auto GetByName_getif(const T_& obj, const char* name) -> T_::ReflectionType::template Member<Ind>::Type {
    if  (T_::Reflection().Names[Ind] == name)  {
        return obj.*(T_::ReflectionType::template Member<Ind>::MemberPointer());
    }
    return {};
}

template <class T_, int ...S_>
auto GetByName_expand(Gorgon::TMP::Sequence<S_...>, const T_& obj, const char * name) {
    return (GetByName_getif<T_, S_>(obj, name), ...);
}

template <class T_>
auto GetByName_(const T_& obj, const char * name) {
    return GetByName_expand(typename Gorgon::TMP::Generate<T_::ReflectionType::MemberCount>::Type{}, obj, name);
}

Fakat yukarıdakinin problemi, ilk returnden sonra duracak olması. İlk yazarken aklıma gelmemişti duracağı. :') Sonrasında std::any'den yararlanıp şöyle bir şey yazdım;
C++:
class Get {
    private:
    std::any value;
    template<class T_, int Ind>
    void ByName_getif(const T_& obj, const std::string& name) {
        value.reset();
        if  (T_::Reflection().Names[Ind] == name)
            value = obj.*(T_::ReflectionType::template Member<Ind>::MemberPointer());
    }

    template <class T_, int ...S_>
    void ByName_expand(Gorgon::TMP::Sequence<S_...>, const T_& obj, const std::string& name) {
        (ByName_getif<T_, S_>(obj, name), ...);
    }

    public:
    template <class T_>
    std::any ByName(const T_& obj, const std::string& name) {
        ByName_expand(typename Gorgon::TMP::Generate<T_::ReflectionType::MemberCount>::Type{}, obj, name);
        return value;
    }
    template <class T_>
    std::any operator()(const T_& obj, const std::string& name) {
        return ByName(obj, name);
    }
};

Get GetByName;

Fakat hala any kullandığım için, cast etmem gerekiyor. cast olmadan otomatik olarak type'ına göre döndürebilmek istiyorum. İyice beynim yanmaya başladı. Nasıl çözebilirim?

@Vavien.
 
Son düzenleyen: Moderatör:
Template yazıp o template'lerin türüne göre özel olarak fonksiyon davranışını programlayabiliyoruz C++'ta. "Type'ına göre döndürmek istiyorum" kısmından bunu anladım en azından.

C++:
template <typename T>
T f(T data){return data;}

template <>
string f(string data){return "str";}
 
Template yazıp o template'lerin türüne göre özel olarak fonksiyon davranışını programlayabiliyoruz C++'ta. "Type'ına göre döndürmek istiyorum" kısmından bunu anladım en azından.

C++:
template <typename T>
T f(T data){return data;}

template <>
string f(string data){return "str";}
Hayır, type deduction yapamaz hala böyle.
Daha net açıklayayım;
Reflection sisteminde biz nesnelerle compile time'da dinamik olarak etkileşime geçiyoruz. Tek tek mesela obj.name obj.foo obj.bar yerine, ["name", "foo", "bar"] bir array ile assign işlemi yapabiliyoruz.
Örneğin şu kısım da, .TMX dosyasından çektiğim dataya göre class dolduruyorum;
C++:
// This filler is written keeping tiled's xml system in mind.
    // It takes the structer as an array, if you are going to give only one object,
    // give it as std::array<Structure, 1> struct;
    // this is a recursive function,
    // if possible it's going to loop.
    // TODO: Handle inner nodes.
    // TODO: Implement a way to find how many times the tags appear in the XML file.
    template<size_t Index, size_t ObjArrSize, class Structure>
    void Fill(std::array<Structure, ObjArrSize>& obj, std::string file_name ,std::string firstNode) {
        // PugiXML parser is used for parsing the data in the first step.
        // this part of the system requires us to use C++ 23 if we are going to use constexpr function.
        // C++ 20 doesn't support constexpr version of this.
        pugi::xml_document doc;
        pugi::xml_parse_result res = doc.load_file(file_name.c_str());
        const auto& name = Structure::tag;
        // if somehow parsing files, Pugi supplies error information.
        // so we throw ParseFaield exception to abort the program.
        // user can ignore this exception by catching it
        if(!res) {
            throw ParseFailed((std::string("Parsing failed with error: ") + res.description()).c_str());
        }

        // for type shortening purposes, this is set.
        auto attrlist = obj[Index].Reflection().Names;
        auto map = doc.child(firstNode.c_str());

        // since the function is recursive, this is necessary to prevent
        // infinite loop
        if constexpr (Index < ObjArrSize) {
            // Pugi doesn't supply binary + operator.
            // TODO: Implement binary + operator for pugi::xml_node.
            // thus we nned to iterate through the iterator
            auto child = map.begin();
            // pugi child function actually gets the first sibling
            // of the child (node), but for continuesly iterating,
            // it doesn't supply a iterator to that child.
            // so we first iterate to first sibling of the wanted node
            // if name doesn't exist, it's gonna go into infinite loop
            // when pugi reaches the end of the file or end of the nodes,
            // it's gonna return an empty string as name.
            while(std::string((*child).name()) != name or std::string((*child).name()) == "") {
                child++;
            }
            // then we iterate to the node we want using Index
            for(int j = 0; j < Index; j++) {
                child++;
            }
            // below is self explonatery, yet let me explain.
            // if the tag set by structure is matches with any child
            // do below;
            if(name == std::string((*child).name())) {
                // set the matching variables with attribute list.
                // example;
                // let's say attribute is width and is in the child,
                // and if it's also in the structure (must use the same name)
                // it'll be set.
                for(int i = 0; i < obj[Index].Reflection().MemberCount; i++) {
                    SetByName(obj[Index], attrlist[i], (*child).attribute(attrlist[i]).value());
                }
                // TODO: handle inners as generic as this is.
                obj[Index].SetInner((*child).first_child());
            };
            // Recursive call
            Fill<Index + 1, ObjArrSize, Structure>(obj, file_name, firstNode);
        }
    }
Kullanıcıdan gelecek class'ın memberlarını bilmiyorum, dosyadan gelecek attributeları bilmiyorum. Bunların hepsinin bu yüzden dinamik olması lazım. Bir nevi ORM gibi düşünebilirsin reflection sistemini.

Şimdi sorun şu, Gorgon'un Struct.h'ı getter ve setter'a sahip değil. Getter ve Setter'ı ben yazıyorum. Setter kolay. Ancak Getter'da sorun bir sınıf birden fazla data tipinde üyelere sahipse ilk üyenin türüne göre dönüyor genelde. Bu da istenmeyen bir durum. Bu durumun çözümünü şimdilik std::any ile buldum ve casting'i kullanıcıya bıraktım. Mesela, üçüncü element bar bir integer, kullanımı şöyle;
any_cast<int>(GetByName(obj, "bar"); . Amacım bu aradaki cast'i eleyip direkt olarak o türdeki nesneyi verebilmek.
 
Merhaba;

Gorgon: Summary üzerinde map vb sistemler için bir şey geliştiriyorum. Gorgon'un içerisinde Struct.h kısmına baktığınızda reflection becerisini olduğunu göreceksiniz. Bu becerisinden yararlanıp setbyname adında bir fonksiyon oluşturdum. Şimdi aynı mantıkla getbyname oluşturmak istiyorum.

Aşağıda SetByName var.
C++:
// Using Gorgon's Reflection system to handle member assignments.
    namespace internal {
        template<class T_, int Ind>
        // Sets the member by name if the name exists in the structure's reflection.
        void SetByName_setif(T_ &obj, const std::string &name, const std::string &value) {
            if(T_::Reflection().Names[Ind] == name)
                obj.*(T_::ReflectionType::template Member<Ind>::MemberPointer()) = Gorgon::String::To<typename T_::ReflectionType::template Member<Ind>::Type>(value);
        }
        // Since we can't travel template members in run time, we use Gorgon Sequence for to do that using template parameters.
        template<class T_, int ...S_>
        void SetByName_expand(Gorgon::TMP::Sequence<S_...>, T_ &obj, const std::string &name, const std::string &value) {
            (SetByName_setif<T_, S_>(obj, name, value), ...);
        }
    }
    // Caller for the above.
    template<class T_>
    void SetByName(T_ &obj, const std::string &name, const std::string &value) {
        internal::SetByName_expand(typename Gorgon::TMP::Generate<T_::ReflectionType::MemberCount>::Type{}, obj, name, value);
    }

Aynı mantıktan yola çıkarak önce şunu yaptım;
C++:
template<class T_, int Ind>
auto GetByName_getif(const T_& obj, const char* name) -> T_::ReflectionType::template Member<Ind>::Type {
    if  (T_::Reflection().Names[Ind] == name)  {
        return obj.*(T_::ReflectionType::template Member<Ind>::MemberPointer());
    }
    return {};
}

template <class T_, int ...S_>
auto GetByName_expand(Gorgon::TMP::Sequence<S_...>, const T_& obj, const char * name) {
    return (GetByName_getif<T_, S_>(obj, name), ...);
}

template <class T_>
auto GetByName_(const T_& obj, const char * name) {
    return GetByName_expand(typename Gorgon::TMP::Generate<T_::ReflectionType::MemberCount>::Type{}, obj, name);
}

Fakat yukarıdakinin problemi, ilk returnden sonra duracak olması. İlk yazarken aklıma gelmemişti duracağı. :') Sonrasında std::any'den yararlanıp şöyle bir şey yazdım;
C++:
class Get {
    private:
    std::any value;
    template<class T_, int Ind>
    void ByName_getif(const T_& obj, const std::string& name) {
        value.reset();
        if  (T_::Reflection().Names[Ind] == name)
            value = obj.*(T_::ReflectionType::template Member<Ind>::MemberPointer());
    }

    template <class T_, int ...S_>
    void ByName_expand(Gorgon::TMP::Sequence<S_...>, const T_& obj, const std::string& name) {
        (ByName_getif<T_, S_>(obj, name), ...);
    }

    public:
    template <class T_>
    std::any ByName(const T_& obj, const std::string& name) {
        ByName_expand(typename Gorgon::TMP::Generate<T_::ReflectionType::MemberCount>::Type{}, obj, name);
        return value;
    }
    template <class T_>
    std::any operator()(const T_& obj, const std::string& name) {
        return ByName(obj, name);
    }
};

Get GetByName;

Fakat hala any kullandığım için, cast etmem gerekiyor. cast olmadan otomatik olarak type'ına göre döndürebilmek istiyorum. İyice beynim yanmaya başladı. Nasıl çözebilirim?

@Vavien.
variant kullanmayı denedin mi ?
 
std::visit - cppreference.com bununla çalışması lazım diye hatırlıyorum.
std::variant'ı kullanabilmem için gelecek typelar sabit olmalı. Yani variant'ın template parameterleri compile time'da belirlenmiş olmalı. Ek olarak visitor birden fazla türü döndüremez;
1713077171203.png

1713077180932.png
 
Amacım bu aradaki cast'i eleyip direkt olarak o türdeki nesneyi verebilmek.
İstediğin bir çözüm değil biliyorum fakat cast işlemini kullanıcıdan kurtarabiliriz.

C++:
template <typename U, class T_>
U operator()(const T_& obj, const std::string& name) {
    return std::any_cast<U>(ByName(obj, name));

// auto bar = GetByName<int>(foo, "bar")
}
 

Yeni konular

Geri
Yukarı