Skip to content

Instantly share code, notes, and snippets.

@yafiyogi
Last active June 6, 2021 23:53
Show Gist options
  • Select an option

  • Save yafiyogi/83942418ba4e74866422638621248e4e to your computer and use it in GitHub Desktop.

Select an option

Save yafiyogi/83942418ba4e74866422638621248e4e to your computer and use it in GitHub Desktop.
C++ Structured Bindings for classes Recipe
/*
* MIT License
*
* Copyright (c) 2021 Yafiyogi
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
// g++ -std=c++17 -O2 structured_bindings.cpp
// Inspired by Raymond Chen: https://devblogs.microsoft.com/oldnewthing/20201015-00/?p=104369
// and by Nicolai M. Josuttis: https://www.cppstd17.com/ chapter 1 section 3
#include <iostream>
#include <string>
#include <utility>
#include <type_traits>
#include <tuple>
namespace yafiyogi {
// Binding copy/reference test class.
class OtherThing
{
public:
OtherThing()
{
++count;
id = count;
std::cout << "OT=default id=" << id << std::endl;
}
OtherThing(const OtherThing &)
{
++count;
id = count;
std::cout << "OT=copy id=" << id << std::endl;
}
OtherThing(OtherThing &&)
{
++count;
id = count;
std::cout << "OT=move id=" << id << std::endl;
}
~OtherThing()
{
--count;
std::cout << "OT=destuct id=" << id << std::endl;
}
static int count;
int id;
};
int OtherThing::count = 0;
// Structured Bindings applied to this class.
class Thing
{
public:
Thing(int t1, std::string t2):
thing_1(t1),
thing_2(t2)
{}
int Thing1() const
{
return thing_1;
}
auto & Thing2() const
{
return thing_2;
}
auto & Thing3() const
{
return thing_3;
}
private:
int thing_1;
std::string thing_2;
OtherThing thing_3;
};
// Getter for Thing class
template<std::size_t Idx,
typename T,
std::enable_if_t<std::is_base_of_v<Thing, std::remove_cv_t<std::remove_reference_t<T>>>, bool> = true>
decltype(auto) get(T && thing) noexcept
{
if constexpr (0 == Idx)
{
return thing.Thing1();
}
if constexpr (1 == Idx)
{
return thing.Thing2();
}
if constexpr (2 == Idx)
{
return thing.Thing3();
}
}
// Getter return value types
using ThingTypes = tuple<int, const std::string, const yafiyogi::OtherThing>;
} // namespace yafiyogi
namespace std {
// Specialise std::tuple_size<> for Thing
template<>
struct tuple_size<yafiyogi::Thing>:
tuple_size<yafiyogi::ThingTypes>
{};
// Specialise std::tuple_element<> for Thing
template<size_t Idx>
struct tuple_element<Idx, yafiyogi::Thing>:
tuple_element<Idx, yafiyogi::ThingTypes>
{
};
// Specialise std::get<> for Thing
// You only need this if you want to do: std::get<0>(thing)
template<size_t Idx,
typename T,
std::enable_if_t<std::is_base_of_v<yafiyogi::Thing, std::remove_cv_t<std::remove_reference_t<T>>>, bool> = true>
decltype(auto) get( T && thing)
{
return yafiyogi::get<Idx>( std::forward<T>(thing));
}
} // namespace std
int main()
{
std::cout << "main 1 OT=" << yafiyogi::OtherThing::count << std::endl;
yafiyogi::Thing thing{13, "poop"};
std::cout << "main 2 OT=" << yafiyogi::OtherThing::count << std::endl;
auto t_g = std::get<0>( thing);
// int one;
// std::string two;
// yafiyogi::OtherThing three;
// This doesn't work.
// std::tie(one, two, three) = thing;
auto [t1, t2, t3] = thing;
std::cout << "main 3 OT=" << yafiyogi::OtherThing::count << std::endl;
auto && [t_1, t_2, t_3] = thing;
std::cout << "main 4 OT=" << yafiyogi::OtherThing::count << std::endl;
const auto & thing_2 = thing;
auto && [t2_1, t2_2, t2_3] = thing;
std::cout << "main 5 OT=" << yafiyogi::OtherThing::count << std::endl;
auto && [t3_1, t3_2, t3_3] = yafiyogi::Thing{1, "pop3"};
std::cout << "main 5 OT=" << yafiyogi::OtherThing::count << std::endl;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment