List WPA supplicant network properties using dbus-cpp - c++

When trying to list network properties - https://w1.fi/wpa_supplicant/devel/dbus.html#dbus_network using dbus-cpp I get a number of errors about missing operator== for core::dbus::types::Variant
/usr/include/core/dbus/impl/object.h:185:17: required from ‘std::shared_ptr<core::dbus::Property<PropertyDescription> > core::dbus::Object::get_property() [with PropertyDescription = fi::w1::wpa_supplicant1::Network::Properties::Propertiez]’ /home/martin/ClionProjects/ang-wifi-controller/src/wpasupplicantclient.cpp:149:118: required from here /usr/include/c++/6/bits/stl_pair.h:364:51: error: no match for ‘operator==’ (operand types are ‘const core::dbus::types::Variant’ and ‘const core::dbus::types::Variant’)
{ return __x.first == __y.first && __x.second == __y.second; }
My code is based on dbus-cpp examples and http://quaintous.com/2015/08/30/cpp11-dbus/, but they only offer limited assistance.
The code representing Properties property is as follows:
namespace fi {
namespace w1 {
struct wpa_supplicant1 {
struct Network {
struct Properties {
struct Propertiez {
inline static std::string name() { return "Properties"; };
typedef Network Interface;
typedef std::map<std::string, core::dbus::types::Variant> ValueType;
static const bool readable = true;
static const bool writable = true;
};
};
};
And the offending line in .cpp is networkProxy->get_property<fi::w1::wpa_supplicant1::Network::Properties::Propertiez>();
I found that this question has already been asked at https://answers.launchpad.net/ubuntu/+source/dbus-cpp/+question/593271, but nobody offered any advice. Going through the code of packages listed by apt-cache rdepends libdbus-cpp5 also yielded no results.
I tried messing with the ValueType but it all resulted in runtime errors as the expected result probably truly is the map. It honestly seems like a bug in the library to me, but since this must be an obvious use case I am trying to find the mistake in my usage of the library. So what am I doing wrong?
Edit: Since I did not get any response, I am including minimal sample.
#include <core/dbus/bus.h>
#include <core/dbus/object.h>
#include <core/dbus/property.h>
#include <core/dbus/service.h>
#include <core/dbus/result.h>
#include <core/dbus/asio/executor.h>
#include <core/dbus/interfaces/properties.h>
#include <core/dbus/types/stl/string.h>
#include <core/dbus/types/stl/tuple.h>
#include <core/dbus/types/stl/vector.h>
#include <core/dbus/types/struct.h>
#include <core/dbus/types/variant.h>
using namespace std::chrono_literals;
using DBusDict = std::map<std::string, core::dbus::types::Variant>;
namespace fi {
namespace w1 {
struct wpa_supplicant1
{
struct GetInterface {
typedef wpa_supplicant1 Interface;
static const std::string &name()
{
static const std::string s("GetInterface");
return s;
}
inline static const std::chrono::milliseconds default_timeout() { return 1s; }
};
struct Iface
{
struct AddNetwork {
typedef Iface Interface;
static const std::string &name()
{
static const std::string s("AddNetwork");
return s;
}
inline static const std::chrono::milliseconds default_timeout() { return 1s; }
};
struct Properties
{
struct Networks
{
inline static std::string name()
{ return "Networks"; };
typedef Iface Interface;
typedef std::vector<core::dbus::types::ObjectPath> ValueType;
static const bool readable = true;
static const bool writable = false;
};
};
};
struct Network
{
struct Properties
{
struct Propertiez
{
inline static std::string name()
{ return "Properties"; };
typedef Network Interface;
typedef DBusDict ValueType;
static const bool readable = true;
static const bool writable = true;
};
};
};
};
};
};
namespace core {
namespace dbus {
namespace traits {
template <> struct Service<fi::w1::wpa_supplicant1> {
inline static const std::string &interface_name()
{
static const std::string s("fi.w1.wpa_supplicant1");
return s;
}
};
template <> struct Service<fi::w1::wpa_supplicant1::Iface> {
inline static const std::string &interface_name()
{
static const std::string s("fi.w1.wpa_supplicant1.Interface");
return s;
}
};
template <> struct Service<fi::w1::wpa_supplicant1::Network> {
inline static const std::string &interface_name()
{
static const std::string s("fi.w1.wpa_supplicant1.Network");
return s;
}
};
}
}
}
int main()
{
//bus
auto systemBus = std::make_shared<core::dbus::Bus>(core::dbus::WellKnownBus::system);
systemBus->install_executor(core::dbus::asio::make_executor(systemBus));
auto busThread = std::thread(std::bind(&core::dbus::Bus::run, systemBus));
//service
auto wpaService = core::dbus::Service::use_service<fi::w1::wpa_supplicant1>(systemBus);
auto wpaObjectPath = core::dbus::types::ObjectPath("/fi/w1/wpa_supplicant1");
auto wpaRootProxy = wpaService->object_for_path(wpaObjectPath);
//iface
auto ifacePath = wpaRootProxy->transact_method<fi::w1::wpa_supplicant1::GetInterface,
core::dbus::types::ObjectPath,
std::string>("wlan0"); //change this to your own wireless interface
auto wpaIfaceProxy = wpaService->object_for_path(ifacePath.value());
auto networkPaths = wpaIfaceProxy->get_property<fi::w1::wpa_supplicant1::Iface::Properties::Networks>();
//network
std::string ssid("network");
std::string password("password");
DBusDict args = {
{"ssid", core::dbus::types::Variant::encode(ssid)},
{"psk", core::dbus::types::Variant::encode(password)},
};
auto networkPath = wpaIfaceProxy->transact_method<fi::w1::wpa_supplicant1::Iface::AddNetwork,
core::dbus::types::ObjectPath,
DBusDict>(args);
auto networkProxy = wpaService->object_for_path(networkPath.value());
//get properties - uncomment line below to get compiler errors
//auto netProps = networkProxy->get_property<fi::w1::wpa_supplicant1::Network::Properties::Propertiez>();
while (true) {
continue;
}
}
Compile using: g++ $(pkg-config --cflags dbus-1 dbus-cpp) ./main.cpp $(pkg-config --libs dbus-1 dbus-cpp) -lpthread

Update:
dbus-cpp has an implementation for org.freedesktop.DBus.Properties.Get method.
Get:
auto resultVariant = dbus_object->invoke_method_synchronously
/*Method*/ <dbus::interfaces::Properties::Get,
/*Output Type*/ dbus::types::Variant,
/*Input Types*/ std::string, std::string>
("fi.w1.wpa_supplicant1.Network","Properties").value();
auto props = resultVariant.as<std::map<std::string, dbus::types::Variant>>();
Sadly, the Set method, while also implemented, does NOT seem to work with any ArgumentTypes with nested Variants within them. So:
a{sv} : std::map<std::string, core::dbus::types::Variant>
a{v} : std::vector<core::dbus::types::Variant>
in a Set method call will actually cause the program to crash. (Didn't test more)
Old post:
I ran into the same bug today and found a workaround for at least getting the properties value.
Instead of using
auto prop = dbus_object->get_property<fi::w1::wpa_supplicant1::Network::Properties::Propertiez>();
try
//returns std::map<std::string, core::dbus::types::Variant>>
auto props = dbus_object->get_all_properties<fi::w1::wpa_supplicant1::Network>();
auto prop = props["Properties"];
auto prop_value = prop.as<std::map<std::string, core::dbus::types::Variant>>();
As far as I understand the bug, dbus-cpp makes use of the
org.freedesktop.DBus.Properties interface to read out Properties.
So dbus_object->get_property() tries to invoke a org.freedesktop.DBus.Properties.Get and fails to compile because of the missing ==operator implementation. (Something it needs for casting the specific ValueType, I guess)
dbus_object->get_all_properties() invokes org.freedesktop.DBus.Properties.GetAll which does not need a specific ValueType, so it works.
Of course this is just a workaround to getting properties, since setting a property value is bound to the same shared_pointer as getting it.

As the documentation for fi.w1.wpa_supplicant1.Network.Properties.Properties says:
[...] All values are string type, e.g., frequency is "2437", not 2437.
So try to define DBusDict as follow:
using DBusDict = std::map<std::string, std::string>;

Related

Boost bimap can't convert const CompatibleKey to Key&

It is one of the first times I am using boost and I am getting an error saying
BaseKey boost::bimaps::container_adaptor::detail::key_to_base_identity<BaseKey,KeyType>::operator ()(Key &) const': cannot convert argument 1 from 'const CompatibleKey' to 'Key &
and
boost::multi_index::detail::ordered_index_impl<KeyFromValue,Compare,SuperMeta,TagList,Category, AugmentPolicy>::find': no matching overloaded function found
I know most of the STL errors or at least where could they come from, but I am not experienced enough with boost to know what could be going on here. The code I have is the following, it is used to convert the values from an enum to strings and vice versa.
file.h
namespace FOO_NS::BAR_NS
{
class FooClass
{
public:
enum class Enum
{
Enum1, Enum2, Enum3, Enum4
};
...
};
namespace
{
using results_bimap = boost::bimap<FooClass::Enum, std::string>;
using position = results_bimap::value_type;
const auto EnumsAsStrings = []() {
results_bimap result;
result.insert(position(FooClass::Enum::Enum1, "Enum1"));
result.insert(position(FooClass::Enum::Enum2, "Enum2"));
result.insert(position(FooClass::Enum::Enum3, "Enum3"));
result.insert(position(FooClass::Enum::Enum4, "Enum4"));
return result;
};
} // namespace
}//namespace FOO_NS::BAR_NS
file.cpp
using namespace FOO_NS::BAR_NS;
void doSmth()
{
...
std::string enumString = EnumsAsStrings().left.at(FooClass::Enum::Enum1); // Expected string "Enum1"
}
Do you see any misconception or misusage I have in this code so that this mentioned error happens?
You don't show enough code. Here's
assuming enum Enum{...}: http://coliru.stacked-crooked.com/a/20455e28883f93be
assuming enum class Enum{...}:
All I can think of is that you might have FooClass defined in an anonymous namespace as well, and you actually have disparate declarations of the enum which are not equivalent to the compiler.
Note that if this kind of setup would be the goal, you should be able to leverage the CompatibleKey overload by using a transparent comparator instead of the default (e.g. less<void> instead of less<FooClass::enum>).
Listing
Anti-bitrot:
#include <boost/bimap.hpp>
struct FooClass{
enum class Enum { Enum1, Enum2, Enum3, Enum4 };
};
namespace {
using results_bimap = boost::bimap<FooClass::Enum, std::string>;
using position = results_bimap::value_type;
auto const EnumsAsStrings = []() {
results_bimap result;
result.insert(position(FooClass::Enum::Enum1, "Enum1"));
result.insert(position(FooClass::Enum::Enum2, "Enum2"));
result.insert(position(FooClass::Enum::Enum3, "Enum3"));
result.insert(position(FooClass::Enum::Enum4, "Enum4"));
return result;
};
} // namespace
int main() {
std::string enumString =
EnumsAsStrings().left.at(FooClass::Enum::Enum1);
assert(enumString == "Enum1");
}
The following MCVE works, so it looks like you're not providing all the relevant information as to what your problem is:
Live Coliru Demo
#include <boost/bimap.hpp>
#include <string>
struct FooClass
{
enum Enum{Enum1,Enum2,Enum3,Enum4};
};
using results_bimap = boost::bimap<FooClass::Enum, std::string>;
using position = results_bimap::value_type;
const auto EnumsAsStrings = []() {
results_bimap result;
result.insert(position(FooClass::Enum1, "Enum1"));
result.insert(position(FooClass::Enum2, "Enum2"));
result.insert(position(FooClass::Enum3, "Enum3"));
result.insert(position(FooClass::Enum4, "Enum4"));
return result;
};
int main()
{
std::string enumString = EnumsAsStrings().left.at(FooClass::Enum1);
}
Okay, so at the end it wasn't anything related to this code (directly). I was calling the lambda like this EnumsAsStrings().left.at(FooClass::Enum1) and it couldn't implicitly convert from FooClass to FooClass::Enum and that was creating the errors. Thank you to everyone who tried to answer my question!

Issue with LLVM JIT (LLJIT)

I am following a tutorial on making JIT compiler with LLVM (code is shown below and most recent version of LLVM is used). Everything works other than this line (if I comment this function, code compiles):
return (T)symbol.get().getAddress();
which gives the following error:
JIT.cpp:22:29: error: ‘std::remove_reference_t<llvm::orc::ExecutorAddr> {aka class llvm::orc::ExecutorAddr}’ has no member named ‘getAddress’
return (T)symbol.get().getAddress();
Code
#pragma once
#include <llvm/ExecutionEngine/Orc/LLJIT.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IR/Module.h>
#include <memory>
#include <type_traits>
namespace FooLang
{
class JIT
{
private:
std::unique_ptr<llvm::orc::LLJIT> lljit;
public:
JIT(std::unique_ptr<llvm::orc::LLJIT> _lljit) : lljit(std::move(_lljit)) {}
void registerSymbols(
llvm::function_ref<llvm::orc::SymbolMap(llvm::orc::MangleAndInterner)> symbolMap) {
auto &mainJitDylib = this->lljit->getMainJITDylib();
llvm::cantFail(mainJitDylib.define(
absoluteSymbols(symbolMap(llvm::orc::MangleAndInterner(
mainJitDylib.getExecutionSession(), this->lljit->getDataLayout())))));
}
template <typename T, typename = std::enable_if_t<std::is_pointer<T>::value && std::is_function<std::remove_pointer_t<T>>::value>>
llvm::Expected<T> lookup(const std::string &name)
{
auto symbol = this->lljit->lookup(name);
if (!symbol)
{
return symbol.takeError();
}
return (T)symbol.get().getAddress();
}
template <typename T, typename = std::enable_if_t<std::is_function<T>::value>>
inline llvm::Expected<T *> lookup(const std::string &name)
{
return this->lookup<T *>(name);
}
static llvm::Expected<JIT> create(std::unique_ptr<llvm::Module> &module, std::unique_ptr<llvm::LLVMContext> &context)
{
auto lljit = llvm::orc::LLJITBuilder().create();
auto &jd = lljit.get()->getMainJITDylib();
jd.addGenerator(llvm::cantFail(llvm::orc::DynamicLibrarySearchGenerator::GetForCurrentProcess('_')));
if (!lljit)
{
return lljit.takeError();
}
if (auto err = lljit.get()->addIRModule(llvm::orc::ThreadSafeModule(std::move(module), std::move(context))))
{
return std::move(err);
}
return JIT(std::move(lljit.get()));
}
};
} // namespace FooLang
And it is used like following:
int run(IRgen* ir_gen)
{
auto jit = JIT::create(ir_gen->module, ir_gen->llvm_context);
jit->registerSymbols(
[&](llvm::orc::MangleAndInterner interner) {
llvm::orc::SymbolMap symbolMap;
// Add symbols here
symbolMap[interner("printf")] = llvm::JITEvaluatedSymbol::fromPointer(printf);
return symbolMap;
});
auto entry = jit->lookup<int()>("main");
if (!entry)
{
llvm::errs() << entry.takeError();
return 1;
}
return entry.get()();
}
In a recent change auto symbol = this->llvmjit->lookup(name); will return an ExecutorAddr instead of a JITEvaluatedSymbols, see: https://reviews.llvm.org/rG16dcbb53dc7968a3752661aac731172ebe0faf64
Your pasted code returns a template parameter type T and doesn't show the caller so I can't see what you intend to return. My suggestion is that you probably want to use ExecutorAddr::toPtr but I can't be sure.

How to adapt a custom polygon type in boost geometry

I am trying to use boost geometry algorithms with my own custom polygon type. But I getting compiler errors (in Visual Studio 2019 Windows 10).
I have simplified what I am trying to do into the following code.
In my_custom_polygon.hpp
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
using point = boost::geometry::model::d2::point_xy<double>;
using ring = boost::geometry::model::ring<point>;
struct MyPoly
{
ring exteriorRing;
std::vector<ring> interiorRings;
};
using polygon = MyPoly;
using multipolygon = std::vector<MyPoly>;
//using polygon = boost::geometry::model::polygon<point>;
//using multipolygon = boost::geometry::model::multi_polygon<polygon>;
namespace boost::geometry::traits
{
template<> struct tag<MyPoly> { using type = polygon_tag; };
template<> struct tag<std::vector<MyPoly>> { using type = multi_polygon_tag; };
template<> struct ring_const_type<MyPoly> { using type = const ring; };
template<> struct ring_mutable_type<MyPoly> { using type = ring; };
template<> struct interior_const_type<MyPoly> { using type = const std::vector<ring>; };
template<> struct interior_mutable_type<MyPoly> { using type = std::vector<ring>; };
template<> struct exterior_ring<MyPoly>
{
static ring& get(MyPoly& poly) { return poly.exteriorRing; }
static const ring& get(const MyPoly& poly) { return poly.exteriorRing; }
};
template<> struct interior_rings<MyPoly>
{
static std::vector<ring>& get(MyPoly& poly) { return poly.interiorRings; }
static const std::vector<ring>& get(const MyPoly& poly) { return poly.interiorRings; }
};
}
In my_custom_polygon.cpp
int main(int argc, const char** argv)
{
const double buffer_distance = 1.0;
const int points_per_circle = 36;
boost::geometry::strategy::buffer::distance_symmetric<double> distance_strategy(buffer_distance);
boost::geometry::strategy::buffer::join_round join_strategy(points_per_circle);
boost::geometry::strategy::buffer::end_round end_strategy(points_per_circle);
boost::geometry::strategy::buffer::point_circle circle_strategy(points_per_circle);
boost::geometry::strategy::buffer::side_straight side_strategy;
multipolygon result;
point p{0.0, 0.0};
boost::geometry::buffer(p, result,
distance_strategy, side_strategy,
join_strategy, end_strategy, circle_strategy);
return 0
}
This fails to compile with an error C2664 in boost/geometry/algorithms/detail/overlay/convert_ring.hpp on line 70
It says it cannot convert argument 2 from 'boost::geometry::model::ring<point,true,true,std::vector,std::allocator>' to 'Geometry2 &'
But if I use the commented out lines in the .hpp file for the polygon and multipolygon types it compiles and runs just fine.
I am obviously not adapting the polygon correctly.
Anybody have any ideas?
Thanks
First thought reading the title... Oh boy: here we go again :)
Fiddling with it for a while (and cleaning up a little), I found that the cause is that ring_{mutable,const}_type::type needs to be references for this algorithm.
This is making me think that earlier adaptations I made were less-than-optimal and leading to unnecessary ring copying, see e.g. Further problems in adapting a geometry object model using boost geometry and (How to) Create own polygon type in boost geometry and use multi_polygon type with it?
So, without further ado:
Live On Coliru
#include <boost/geometry.hpp>
#include <boost/geometry/geometries/geometries.hpp>
#include <iostream>
namespace bg = boost::geometry;
using Point = bg::model::d2::point_xy<double>;
using Ring = bg::model::ring<Point>;
using Rings = std::vector<Ring>;
struct MyPoly {
Ring exteriorRing;
Rings interiorRings;
};
using MyMultiPoly = std::vector<MyPoly>;
namespace boost::geometry::traits {
template <> struct tag<MyPoly> { using type = polygon_tag; };
template <> struct ring_mutable_type<MyPoly> { using type = Ring&; };
template <> struct ring_const_type<MyPoly> { using type = const Ring&; };
template <> struct interior_mutable_type<MyPoly> { using type = Rings; };
template <> struct interior_const_type<MyPoly> { using type = const Rings; };
template<> struct exterior_ring<MyPoly> {
static auto& get(MyPoly& poly) { return poly.exteriorRing; }
static auto& get(const MyPoly& poly) { return poly.exteriorRing; }
};
template<> struct interior_rings<MyPoly> {
static auto& get(MyPoly& poly) { return poly.interiorRings; }
static auto& get(const MyPoly& poly) { return poly.interiorRings; }
};
} // namespace boost::geometry::traits
namespace boost::geometry::traits {
template <> struct tag<MyMultiPoly> { using type = multi_polygon_tag; };
} // namespace boost::geometry::traits
int main() {
MyMultiPoly result;
Point p{0.0, 0.0};
namespace bs = bg::strategy::buffer;
const double buffer_distance = 1.0;
const int points_per_circle = 36;
bs::distance_symmetric<double> distance(buffer_distance);
bs::join_round join(points_per_circle);
bs::end_round end(points_per_circle);
bs::point_circle circle(points_per_circle);
bs::side_straight side;
bg::buffer(p, result, distance, side, join, end, circle);
std::cout << "result: " << bg::wkt(result) << "\n";
}
Prints
result: MULTIPOLYGON(((1 0,0.984808 -0.173648,0.939693 -0.34202,0.866025 -0.5,0.766044 -0.642788,0.642788 -0.766044,0.5 -0.866025,0.34202 -0.939693,0.173648 -0.984808,6.12323e-17 -1,-0.173648 -0.984808,-0.34202 -0.939693,-0.5 -0.866025,-0.642788 -0.766044,-0.766044 -0.642788,-0.866025 -0.5,-0.939693 -0.34202,-0.984808 -0.173648,-1 -1.45473e-15,-0.984808 0.173648,-0.939693 0.34202,-0.866025 0.5,-0.766044 0.642788,-0.642788 0.766044,-0.5 0.866025,-0.34202 0.939693,-0.173648 0.984808,-2.84823e-15 1,0.173648 0.984808,0.34202 0.939693,0.5 0.866025,0.642788 0.766044,0.766044 0.642788,0.866025 0.5,0.939693 0.34202,0.984808 0.173648,1 0)))

Determine whether a type exists without feature-test macro

I'd like to determine whether a type exists without using a feature-test macro. Here's the idea using a macro:
namespace real
{
struct foo final { static constexpr const char* name = "real::foo"; };
}
#define real_foo_ 314
struct my_foo final { static constexpr const char* name = "my_foo"; };
namespace real
{
#if !real_foo_
using foo = my_foo;
#endif
}
That is, real::foo should to the "real" foo if it exists, otherwise my_foo will be used; subsequent code uses real::foo w/o knowing or caring whether it's the actual version or the replacement.
Achiving the same template meta-programming seems to be the right idea:
#include <type_traits>
namespace real
{
struct bar final { static constexpr const char* name = "real::bar"; };
}
struct my_bar final { static constexpr const char* name = "my_bar"; };
// https://devblogs.microsoft.com/oldnewthing/20190710-00/?p=102678
template<typename, typename = void>
constexpr bool is_type_complete_v = false;
template<typename T>
constexpr bool is_type_complete_v<T, std::void_t<decltype(sizeof(T))>> = true;
namespace real { struct bar; }
namespace real
{
using bar = std::conditional_t<is_type_complete_v<real::bar>, real::bar, my_bar>;
}
The above works as shown:
#include <iostream>
int main()
{
real::foo foo;
std::cout << foo.name << "\n";
real::bar bar;
std::cout << bar.name << "\n";
}
But removing the actual definition (not the forward) of real::bar causes compiler errors:
namespace real
{
//struct bar final { static constexpr const char* name = "real::bar"; };
}
error C2371: 'real::bar': redefinition; different basic types
message : see declaration of 'real::bar'
error C2079: 'bar' uses undefined struct 'real::bar'
Is there a way to make real::bar work like real::foo without relying on a feature-test macro? Note that as with real::foo, the name of real::bar can't be changed (e.g., to real::really_bar).
(Actual use case: C++14/C++17/C++20 library features implemented in C++11; once client code using std::filesystem::path has been written, it shouldn't have to change.)
Compiler complains on your code because you are trying to create two entities with same name. Consider changing
namespace real
{
struct bar final { static constexpr const char* name = "real::bar"; };
}
to something like
namespace real
{
struct absolutely_bar final { static constexpr const char* name = "real::bar"; };
}
...
namespace real { struct absolutely_bar; }
namespace real
{
using bar = std::conditional_t<is_type_complete_v<real::absolutely_bar>, real::absolutely_bar, my_bar>;
}
PS: creating such aliases is usually a bad pattern since it's not obvious.

How to access parent scopes recursively?

I tried to do a fancy macro to have some debug informations: The name of the scope you currently are! This can be picked up by e.g. an assert. I tried to made it recursive:
// Global namespace
struct ScopeDef{ static const char* GetName() {return "";} };
typedef ScopeDef ScopeDefParent;
// Macro to place into your namespace/scope
#define NG_SCOPEDEF(scopename) \
struct ScopeDef { \
static const char* GetName() {return scopename;} \
typedef ScopeDefParent Parent; \
}; \
typedef ScopeDef ScopeDefParent;
And using it like:
// Recursive template for testing
template< class T > void PrintImpl() {
PrintImpl< T::Parent >();
printf("::%s", T::GetName() );
}
template<> void PrintImpl< ::ScopeDef >() {}
template< class T > void PrintS() { PrintImpl<T>(); printf("\n");}
// Lets try it:
namespace First {
NG_SCOPEDEF( "First" );
namespace Second {
NG_SCOPEDEF( "Second" );
static void AFun() {
// This prints "::First::Second"
PrintS<ScopeDef>();
}
}
struct Third {
NG_SCOPEDEF( "Third" );
static void BFun() {
// This is endless recursion
PrintS<ScopeDef>();
}
};
}
It doesn't work in class scopes, because the order of definitions don't matter.
This is not a good solution. So is there a way to access the parent scope in a way? In regular code I would just qualify ("::First::ScopeDef"), but that's nothing for a macro.
You could do something like this in C++, where you put in the macro whenever you open a scope and let the destructor take care of cleanup when the scope exits. This example will print out the full scope to stderr, and the output of this code is as follows.
main
main::First
main::First::Second
main::First::Second::DummyClass::DummyFunction
main::First
main
Here is the code:
#include <stdio.h>
class NG_SCOPE_CLASS;
NG_SCOPE_CLASS* NG_SCOPE_END = 0;
class NG_SCOPE_CLASS
{
public:
NG_SCOPE_CLASS(const char* scope)
{
_scope = scope;
_prev = NG_SCOPE_END;
NG_SCOPE_END = this;
}
~ NG_SCOPE_CLASS()
{
NG_SCOPE_END = _prev;
}
void PrintScope()
{
if(_prev)
{
_prev->PrintScope();
fprintf(stderr, "::");
}
fprintf(stderr, "%s", _scope);
}
private:
NG_SCOPE_CLASS* _prev;
const char* _scope;
};
#define NG_SCOPE_PRINT { if(NG_SCOPE_END) { NG_SCOPE_END->PrintScope(); fprintf(stderr, "\n"); } }
#define NG_SCOPE(X) NG_SCOPE_CLASS _NG_SCOPE_CLASS(X)
// THAT'S IT FOR THE DEFINITIONS ABOVE, BELOW IS JUST SOME SAMPLE CODE.
class DummyClass
{
public:
void DummyFunction()
{
NG_SCOPE("DummyClass::DummyFunction");
NG_SCOPE_PRINT;
}
};
int main(int argc, const char* argv[])
{
NG_SCOPE("main");
NG_SCOPE_PRINT;
{
NG_SCOPE("First");
NG_SCOPE_PRINT;
{
NG_SCOPE("Second");
NG_SCOPE_PRINT;
DummyClass theDummyInstance;
theDummyInstance.DummyFunction();
}
NG_SCOPE_PRINT;
}
NG_SCOPE_PRINT;
}
For completeness, our working solution:
#if defined(_MSC_VER)
// GCC solution below. Note MSVC gives warning about unused typedefs but can be suppressed.
#define NG_SCOPEDEF(scopename) \
struct ScopeDefTag { static const char* Name(){return (scopename);}; }; \
typedef ::Scopes::Impl< ScopeDefTag, ScopeDef > ScopeDefHelper; \
struct ScopeDef : ScopeDefHelper {}
#else
// GCC seems to not look ahead.
#define NG_SCOPEDEF(scopename) \
struct ScopeDefTag { static const char* Name(){return (scopename);}; struct SDH : ::Scopes::Impl< ScopeDefTag, ScopeDef >{}; }; \
struct ScopeDef : ScopeDefTag::SDH {}
#endif
namespace Scopes {
struct Chain {
const char* const m_Lit;
const Chain* const m_Prev;
Chain(const char* lit, const Chain* prev) :m_Lit(lit), m_Prev(prev) {}
};
template< bool DMY = true >
struct RootScopeDef
{
static const Chain chain;
static const Chain* Get() { return &chain; }
};
// Being template just to have this in header:
template< bool DMY > const Chain RootScopeDef<DMY>::chain = Chain( "", 0 );
template< class TAG, class PARENT >
struct Impl {
static const Chain chain;
static const Chain* Get() { return &chain; }
typedef PARENT Parent;
};
template< class TAG, class PARENT >
const Chain Impl<TAG, PARENT>::chain = Chain( TAG::Name(), &PARENT::chain );
} // namespace
// Global namespace
typedef Scopes::RootScopeDef<> ScopeDef;
It is based on loopholes in the compiler and is not standard compliant! MSVS thinks that the ScopeDef used as template argument isn't the following one, since it is depending on that very typedef, so it resolves to the parent one, which will be shadowed after. This also works when the macro is placed in a template, because MSVS instantiates them lazily. GCC instead seems to just not look ahead and resolves the base of SDH to the correct one. MSVS would produce an infinite loop of m_Prev references here.
Bottom line: this gives you a nice way to print out namespaces and classes for debugging purposes, but also serves as types for e.g. specializations of templates!