Using C++ in the Bing Maps Trip Optimizer sample
This document describes the C++ component of Bing Maps Trip Optimizer. The C++ component uses pure native code to implement the logic of the route optimization and Visual C++ component extensions (C++/CX) code to interface with other Windows Runtime components.
Note
For more information about C++/CX, the language extensions that are available to a Windows 8.x Store app written in C++, see Visual C++ Language Reference (C++/CX).
For information about how to create a basic C++ Windows Runtime component, see Walkthrough: Creating a basic Windows Runtime component in C++ and calling it from JavaScript or C#.
The C++ part of the app is written as a Windows Runtime dynamic-link library (DLL). This library provides this functionality:
To retrieve the latitude and longitude of each location from the Bing Maps Web service. This operation uses the Bing Maps Representational State Transfer (REST) interface.
To retrieve the travel distance between each possible pair of points in the trip. This operation also uses the Bing Maps REST interface.
To perform the route optimization. This operation uses the ant colony optimization algorithm and parallel processing to efficiently compute the optimized route.
These operations are performed asynchronously, or in the background. This enables the user interface to remain responsive during the optimization process.
Here are some of the key features and technologies that the C++ component uses:
concurrency::create_async to create a Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress> object. The JavaScript app uses this object to monitor progress and completion, and to cancel, the optimization task. The component uses concurrency::task to communicate with Bing Maps and to perform the optimization task in the background.
Bing Maps REST Services to retrieve location and route information.
IXMLHTTPRequest2 to asynchronously request and read HTTP streams from the Bing Maps REST service.
Windows::Data::Xml::Dom::XmlDocument to read responses from the Bing Maps Web service.
The concurrency::parallel_for_each algorithm to parallelize the route optimization algorithm.
Note
The sample code that corresponds to this document is found in the Bing Maps trip optimizer sample.
In this document
Code conventions
In addition to using the Windows Runtime to create the Windows 8.x Store component, the C++ component uses modern code conventions such as smart pointers and exception handling.
The Windows Runtime is a programming interface that you can use to create Windows 8.x Store apps that run only in a trustworthy operating system environment. Such apps use authorized functions, data types, and devices, and are distributed from the Windows Store. The Windows Runtime is represented by the Application Binary Interface (ABI). The ABI is an underlying binary contract that makes Windows Runtime APIs available to programming languages such as Visual C++.
To make it easier to write apps that use the Windows Runtime, Microsoft provides language extensions to Visual C++ that specifically support the Windows Runtime. Many of these language extensions resemble the syntax for the C++/CLI language. However, instead of targeting the common language runtime (CLR), native apps use this syntax to target the Windows Runtime. The hat (^) modifier is an important part of this new syntax because it enables the automatic deletion of runtime objects by means of reference counting. Instead of calling methods such as AddRef and Release to manage the lifetime of a Windows Runtime object, the runtime deletes the object when no other component references it, for example, when it leaves scope or you set all references to nullptr. Another important part of Visual C++ for creating Windows 8.x Store apps is the ref new keyword. Use ref new instead of new to create reference-counted Windows Runtime objects. For more information, see Windows Runtime objects and ref new.
Important
You only have to use ^ and ref new when you create Windows Runtime objects or create Windows Runtime components. You can use the standard C++ syntax when you write core application code that doesn't use the Windows Runtime.
Note
Bing Maps Trip Optimizer uses ^ together with Microsoft::WRL::ComPtr, std::shared_ptr, and std::unique_ptr to manage heap-allocated objects and minimize memory leaks. We recommend that you use ^ to manage the lifetime of Windows Runtime variables, Microsoft::WRL::ComPtr to manage the lifetime of COM variables, and shared_ptr or unique_ptr to manage the lifetime of all other C++ objects that are allocated on the heap.
For more information about modern C++ programming, see Welcome Back to C++ (Modern C++). For more information about the language extensions that are available to a C++ Windows 8.x Store app, see Visual C++ Language Reference (C++/CX).
[]60dddf21-e817-4463-9417-a69b79e5e285#top
File organization
The following list briefly describes the role of each source code file that's part of the C++ component.
AntSystem.h, AntSystem.cpp
Defines the ant colony optimization algorithm and its supporting data structures.HttpRequest.h, HttpRequest.cpp
Defines the HttpRequest class, which is a helper class for performing asynchronous HTTP requests.pch.h, pch.cpp
The precompiled header for the project.TripOptimizer.h, TripOptimizer.cpp
Defines the TripOptimizer class, which serves as the interface between the app and the core component logic.TripOptimizerImpl.h, TripOptimizerImpl.cpp
Defines the TripOptimizerImpl class, which defines the core logic of the component.
[]60dddf21-e817-4463-9417-a69b79e5e285#top
Creating the TripOptimizer and TripOptimizerImpl classes
The C++ component contains a Windows Runtime class, TripOptimizerComponent::TripOptimizer, which can interface with other Windows Runtime components. In Bing Maps Trip Optimizer, this class interfaces with the JavaScript part of the app. (Because the C++ component is written as a DLL, you can also use it with other apps.) The TripOptimizer class defines only the methods that communicate with other Windows Runtime components. The implementation details are handled by the TripOptimizerImpl class. We chose this pattern to better encapsulate the public interface and separate it from the implementation details. For more information about this pattern, see Pimpl For Compile-Time Encapsulation (Modern C++).
// Defines the TripOptimizer class. This class interfaces between the app
// and the implementation details.
public ref class TripOptimizer sealed
{
public:
TripOptimizer();
// Optimizes a trip as an asynchronous process.
Windows::Foundation::IAsyncOperationWithProgress<
Windows::Foundation::Collections::IMap<
Platform::String^,
Windows::Foundation::Collections::IVector<Platform::String^>^>^,
Platform::String^>^ OptimizeTripAsync(
Windows::Foundation::Collections::IVector<Platform::String^>^ waypoints,
Platform::String^ travelMode,
Platform::String^ optimize,
Platform::String^ bingMapsKey,
double alpha, double beta, double rho,
unsigned int iterations, bool parallel);
private:
~TripOptimizer();
// Defines implementation details of the optimization routine.
std::unique_ptr<Details::TripOptimizerImpl> m_impl;
};
Important
When you create a Windows Runtime component class that can be shared with other external components, make sure that you use the public and sealed keywords. For more information about how to create a Windows Runtime component in C++ that's callable from a Windows 8.x Store app, see Creating Windows Runtime Components in C++.
The TripOptimizer::OptimizeTripAsync method is how an app communicates with the C++ component. This method starts the sequence of tasks that compute the optimized trip. The caller uses the return value to monitor progress and completion of the optimization task. It is also used to cancel the operation. Cancellation is explained in the section Handling cancellation later in this document.
The TripOptimizer::OptimizeTripAsync method defers to the TripOptimizerImpl class to perform its action. TripOptimizerImpl is described in more detail later in this document.
// Optimizes a trip as an asynchronous process.
IAsyncOperationWithProgress<IMap<String^, IVector<String^>^>^, String^>^
TripOptimizer::OptimizeTripAsync(
IVector<String^>^ waypoints,
String^ travelMode,
String^ optimize,
String^ bingMapsKey,
double alpha, double beta, double rho,
unsigned int iterations, bool parallel)
{
// Forward to implementation.
return m_impl->OptimizeTripAsync(waypoints,travelMode, optimize, bingMapsKey,
alpha, beta, rho, iterations, parallel);
}
[]60dddf21-e817-4463-9417-a69b79e5e285#top
Component workflow
The TripOptimizer::OptimizeTripAsync method starts the sequence of operations that compute the optimized trip. This method behaves asynchronously to enable the app to remain responsive. To enable asynchronous behavior, this method returns Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>. A Windows Runtime component that calls this method can use this object to get the return value when it's available. This interface also enables the caller to monitor the progress of the operation and receive any errors that occur.
Tip
As a recommended practice, the name of an asynchronous method should end with Async and the method should return Windows::Foundation::IAsyncAction, Windows::Foundation::IAsyncActionWithProgress<TProgress>, Windows::Foundation::IAsyncOperation<TResult>, or Windows::Foundation::IAsyncOperationWithProgress<TResult, TProgress>.
Each Windows Runtime-enabled language (C++, JavaScript, and so on) prescribes its own way to create an asynchronous operation. In C++, you can use the concurrency::create_async function. This function returns an IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult>, or IAsyncOperationWithProgress<TResult, TProgress> object. The return type depends on the signature of the function object that you pass to it. For example, because the TripOptimizerImpl::InternalOptimizeTripAsync method takes a concurrency::progress_reporter object as its parameter and returns a non-void value, create_async returns IAsyncOperationWithProgress<TResult, TProgress>. If this method were to return void, create_async would return IAsyncActionWithProgress<TProgress>. For more information about create_async and how it works with Windows 8, see Creating Asynchronous Operations in C++ for Windows Store Apps.
The following code shows the TripOptimizerImpl::OptimizeTripAsync method.
// Optimizes a trip as an asynchronous process.
IAsyncOperationWithProgress<IMap<String^, IVector<String^>^>^, String^>^
TripOptimizerImpl::OptimizeTripAsync(
IVector<String^>^ waypoints,
String^ travelMode,
String^ optimize,
String^ bingMapsKey,
double alpha, double beta, double rho,
unsigned int iterations, bool parallel)
{
// Copy inputs to a OptimizeTripParams structure.
auto params = make_shared<OptimizeTripParams>();
for (auto waypoint : waypoints)
{
params->Waypoints.push_back(waypoint->Data());
}
params->TravelMode = wstring(travelMode->Data());
params->Optimize = wstring(optimize->Data());
params->BingMapsKey = UriEncode(bingMapsKey->Data());
params->Alpha = alpha;
params->Beta = beta;
params->Rho = rho;
params->Iterations = iterations;
params->Parallel = parallel;
// Perform the operation asynchronously.
return create_async([this, params](progress_reporter<String^> reporter,
cancellation_token cancellationToken) -> task<IMap<String^, IVector<String^>^>^>
{
// Create a linked source for cancellation.
// This enables both the caller (through the returned
// IAsyncOperationWithProgress object) and this class to set
// the same cancellation token.
m_cancellationTokenSource =
cancellation_token_source::create_linked_source(cancellationToken);
// Perform the trip optimization.
return InternalOptimizeTripAsync(params, cancellationToken, reporter)
.then([this](task<IMap<String^, IVector<String^>^>^> previousTask) -> IMap<String^, IVector<String^>^>^
{
try
{
return previousTask.get();
}
catch (const task_canceled&)
{
return nullptr;
}
});
});
}
The progress_reporter object communicates progress messages to the caller. The cancellation_token object enables the component to respond to cancellation requests. (Cancellation is described in the section Handling cancellation in this document.) For more information about how the JavaScript part of the app works with this asynchronous operation that TripOptimizer::OptimizeTripAsync returns, see Interoperating between JavaScript and C++ in the Bing Maps Trip Optimizer sample.
The work function that is provided to create_async in TripOptimizer::OptimizeTripAsync returns a task object. You can return a value, T, or a task, task<T>, from create_async. We return task<T> so that we do not have to wait on the result of the background tasks. Rather, we let the runtime retrieve the result when it’s available and pass that result to the caller. We recommend that you follow this pattern when possible.
The TripOptimizer.cpp file defines the task_from_result helper function that returns a task object that completes immediately with the provided result. This function is useful when you write a function that returns task and that function returns early with a specific result.
// Creates a task that completes with the provided result.
template <typename Result>
task<Result> task_from_result(Result result)
{
return create_task([result]() -> Result { return result; });
}
The following illustration shows the flow of operations that occur when an external component calls TripOptimizer::OptimizeTripAsync to start the optimization process.
The TripOptimizerImpl::OptimizeTripAsync method calls TripOptimizerImpl::InternalOptimizeTripAsync method as part of the asynchronous operation. The TripOptimizerImpl::InternalOptimizeTripAsync method calls TripOptimizerImpl::CreateGraph to create a graph that represents the trip. Each location is represented by a node, and each pair of nodes is connected by an edge. A node contains information about a location, such as its name, its latitude, and its longitude. An edge holds the travel distance between a pair of nodes.
// Creates the graph of objects that represents the trip topography.
void TripOptimizerImpl::CreateGraph(
const vector<wstring>& waypoints,
vector<shared_ptr<Node>>& nodes,
vector<shared_ptr<Edge>>& edges)
{
//
// Create a Node object for each waypoint in the array.
// Each element of the waypoints array contains a string that represents
// a location (for example, "Space Needle, WA").
//
for (const wstring& waypoint : waypoints)
{
// Add a Node object to the collection.
nodes.push_back(make_shared<Node>(waypoint));
}
//
// Create edges that form a fully-connected graph.
//
// Connect every node to every other node.
for (auto iter = begin(nodes); iter != end(nodes); ++iter)
{
auto node1 = *iter;
for_each(iter + 1, end(nodes), [this, &node1, &edges](shared_ptr<Node> node2)
{
// Create edge pair.
edges.push_back(make_shared<Edge>(node1, node2));
});
}
}
The TripOptimizerImpl::InternalOptimizeTripAsync method performs the trip optimization in three phases. In the first phase, this method retrieves location data from Bing Maps. In the second phase, this method retrieves route information between each possible pair of points in the trip. In the third phase, this method performs the trip optimization algorithm. Each phase acts as a task continuation of the previous phase. Continuations allow for you to run one or more tasks after another task completes. Continuations are described in greater detail later in this document. However, note that because a continuation runs in the background, you need to store variables that are shared among tasks so that the tasks can later access them. The TripOptimizerImpl class defines the OptimizeTripParams structure. This structure holds the inputs to the TripOptimizerImpl::InternalOptimizeTripAsync method and variables that are shared among the tasks that comprise the overall operation.
// Holds variables that are used throughout the trip optimization process.
// We create this stucture so that common parameters can be easily passed among
// task continuations.
struct OptimizeTripParams
{
//
// The inputs to OptimizeTripAsync
std::vector<std::wstring> Waypoints;
std::wstring TravelMode;
std::wstring Optimize;
std::wstring BingMapsKey;
double Alpha;
double Beta;
double Rho;
unsigned long Iterations;
bool Parallel;
//
// Timer variables
// The following times are sent as part of a progress message.
// The overall time.
ULONGLONG TotalTime;
// The overall time and the spent performing HTTP requests.
ULONGLONG HttpTime;
// The time spent performing the optimization algorithm.
ULONGLONG SimulationTime;
//
// Location graph.
// A collection of Node objects. There is one Node object for each location.
std::vector<std::shared_ptr<AntSystem::Node>> Nodes;
// A collection of Edge objects. There are
// (n * (n - 1) / 2) edges, where n is the number of nodes.
std::vector<std::shared_ptr<AntSystem::Edge>> Edges;
// The number of pending HTTP requests for the current batch.
long RequestsPending;
// Holds the unresolved locations for the first phase of the optimization process.
concurrency::concurrent_vector<std::shared_ptr<AntSystem::Node>> UnresolvedLocations;
};
The TripOptimizerImpl::OptimizeTripAsync method creates a OptimizeTripParams structure (by using a std::shared_ptr object) and passes it to each of the continuations in the task chain.
// Copy inputs to a OptimizeTripParams structure.
auto params = make_shared<OptimizeTripParams>();
for (auto waypoint : waypoints)
{
params->Waypoints.push_back(waypoint->Data());
}
params->TravelMode = wstring(travelMode->Data());
params->Optimize = wstring(optimize->Data());
params->BingMapsKey = UriEncode(bingMapsKey->Data());
params->Alpha = alpha;
params->Beta = beta;
params->Rho = rho;
params->Iterations = iterations;
params->Parallel = parallel;
Note
We could have created these variables as direct members of the TripOptimizerImpl class. However, by providing a parameter structure, we more clearly associate the state of a trip optimization operation with its action. If we used member variables, it would be more difficult to support features such as running multiple trip optimizations concurrently.
Phase 1: Retrieving location data
In the first phase, the TripOptimizerImpl::InternalOptimizeTripAsync method retrieves location data from Bing Maps. Location data is retrieved so that the user can confirm that Bing Maps has the correct input. For example, if you specify "Pittsburgh", Bing Maps doesn't know if you mean "Pittsburgh, PA", "Pittsburgh, ON", or "Pittsburgh, GA". The C++ component later has to retrieve the travel distance between each pair of locations. Therefore, if any location is ambiguous, all possible locations must be shown in the UI so that the user can select one or enter a new one. The OptimizeTripParams::UnresolvedLocations variable tracks unresolved locations. If the TripOptimizerImpl::RetrieveLocationsAsync method fills this vector with any values, TripOptimizerImpl::InternalOptimizeTripAsync returns them.
//
// Phase 1: Retrieve the location of each waypoint.
//
//
params->HttpTime = GetTickCount64();
// Report progress.
reporter.report("Retrieving locations (0% complete)...");
auto tasks = RetrieveLocationsAsync(params, cancellationToken, reporter);
// Move to the next phase after all current tasks complete.
return when_all(begin(tasks), end(tasks)).then(
[=](task<void> t) -> task<IMap<String^, IVector<String^>^>^>
{
// The PPL requires that all exceptions be caught and handled.
// If any task in the collection errors, ensure that all errors in the entire
// collection are observed at least one time.
try
{
// Calling task.get will cause any exception that occurred
// during the task to be thrown.
t.get();
}
catch (Exception^)
{
observe_all_exceptions<void>(begin(tasks), end(tasks));
// Rethrow the original exception.
throw;
}
catch (const task_canceled&)
{
observe_all_exceptions<void>(begin(tasks), end(tasks));
// Rethrow the original exception.
throw;
}
// Report progress.
reporter.report("Retrieving locations (100% complete)...");
// If there are unresolved locations, return them.
if (params->UnresolvedLocations.size() > 0)
{
// Parallel arrays of input names to their possible resolved names.
Vector<String^>^ inputNames;
Vector<String^>^ options;
auto locations = ref new Map<String^, IVector<String^>^>();
// For each unresolved location, add the user input name and
// the available options to the arrays.
for (size_t i = 0; i < params->UnresolvedLocations.size(); ++i)
{
auto node = params->UnresolvedLocations[i];
// Add options.
options = ref new Vector<String^>();
for (size_t j = 0; j < node->Names.size(); ++j)
{
options->Append(ref new String(node->Names[j].c_str()));
}
locations->Insert(ref new String(node->InputName.c_str()), options);
}
return task_from_result<IMap<String^, IVector<String^>^>^>(locations);
}
The TripOptimizerImpl::RetrieveLocationsAsync method uses the concurrency::task class to process HTTP requests as background tasks. Because the HttpRequest class acts asynchronously, TripOptimizerImpl::RetrieveLocationsAsync holds all background tasks and uses the concurrency::when_all function to define the work that happens next after they finish. Because the TripOptimizerImpl::RetrieveLocationsAsync method is part of the overall background task, this chain of asynchronous operations tasks does not block the main application.
The details about how the HttpRequest class works are explained in the section Defining HTTP functionality in this document.
// Retrieves information about the locations from the Bing Maps location service.
vector<task<void>> TripOptimizerImpl::RetrieveLocationsAsync(
shared_ptr<OptimizeTripParams> params,
cancellation_token cancellationToken,
progress_reporter<String^> reporter)
{
// Holds the tasks that process the returned XML documents.
vector<task<void>> tasks;
// Create HTTP requests for location information.
auto nodes = params->Nodes;
params->RequestsPending = static_cast<long>(params->Nodes.size()); // Used to report progress.
for (auto node : nodes)
{
wstringstream uri;
uri << L"http://dev.virtualearth.net/REST/v1/Locations?q="
<< UriEncode(node->InputName)
<< L"&o=xml&key=" << params->BingMapsKey;
// Create a parent task that downloads the XML document.
auto httpRequest = make_shared<HttpRequest>();
auto downloadTask = httpRequest->GetAsync(
ref new Uri(ref new String(uri.str().c_str())),
cancellationToken);
// Create a continuation task that fills location information after
// the download finishes.
tasks.push_back(downloadTask.then([=](task<wstring> previousTask)
{
(void)httpRequest;
// Get the result to force exceptions to be thrown.
wstring response = previousTask.get();
try
{
// Create and load the XML document from the response.
XmlDocument^ xmlDocument = ref new XmlDocument();
auto xml = ref new String(response.c_str() + 1); // Bypass BOM.
xmlDocument->LoadXml(xml);
// Fill in location information.
ProcessLocation(node, xmlDocument, params->UnresolvedLocations);
}
catch (Exception^)
{
// An error occurred. Cancel any active operations.
m_cancellationTokenSource.cancel();
// Rethrow the exception.
throw;
}
// Report progress.
wstringstream progress;
progress << L"Retrieving locations ("
<< static_cast<long>(100.0 * (params->Nodes.size() - params->RequestsPending) / params->Nodes.size())
<< L"% complete)...";
reporter.report(ref new String(progress.str().c_str()));
InterlockedDecrement(¶ms->RequestsPending);
}));
}
return tasks;
}
The TripOptimizerImpl::RetrieveLocationsAsync method also uses the concurrency::task::then method to processes each response from Bing Maps as it arrives. The task::then method creates a continuation task, which is a task that runs after the previous, or antecedent, task finishes. The call to when_all at the end of the TripOptimizerImpl::RetrieveLocationsAsync method waits for all tasks and their continuation tasks to finish. For more information about tasks and continuations in C++, see Task Parallelism.
The Bing Maps REST API returns XML data. The TripOptimizerImpl::ProcessLocation method loads location information for a location from the provided XML data stream. This method uses XmlDocument::SelectSingleNodeNS to process the provided XmlDocument object. This example shows how the TripOptimizerImpl::ProcessLocation method retrieves the response code for the request:
// Move to response code.
// Report an error and return if the status code is
// not 200 (OK).
xmlNode = xmlDocument->SelectSingleNodeNS(L"/d:Response/d:StatusCode/text()", ns);
if (xmlNode == nullptr) throw ref new NullReferenceException("Failed to parse status code from HTTP response");
Important
Ensure that you use the text() notation when you use XmlDocument::SelectSingleNodeNS to select text nodes from an XML document.
The TripOptimizerImpl::ProcessLocation method throws an exception if an error occurs. In this example, TripOptimizerImpl::ProcessLocation throws Platform::NullReferenceException if the XML document doesn't contain the expected data. Because this error is not recoverable, the component does not catch or this exception. Therefore, if an exception occurs, it's passed to the error handler in the main application.
The TripOptimizerImpl::ProcessLocation method reads the number of total resources from the XML stream. A resource refers to a possible match for a location name. For example, if you specify "Pittsburgh", Bing Maps might return "Pittsburgh, PA", "Pittsburgh, ON", and "Pittsburgh, GA" as the possibilities. Then for each resource, TripOptimizerImpl::ProcessLocation populates the corresponding Node object by using the latitude and longitude of the location. If more than one resource is returned, the TripOptimizerImpl::ProcessLocation method adds the node to the OptimizeTripParams::UnresolvedLocations variable.
// If there is only a single name, set it as the resolved name and
// location.
if (node->Names.size() == 1)
{
node->ResolvedLocation = node->Locations.front();
node->ResolvedName = node->Names.front();
}
// Otherwise, add the node to the list of unresolved locations.
else if (node->ResolvedName.length() == 0)
{
unresolvedLocations.push_back(node);
}
If the OptimizeTripParams::UnresolvedLocations variable contains no elements, the TripOptimizerImpl::InternalOptimizeTripAsync method moves to the second phase, which is to retrieve route data from Bing Maps.
For more information about the Bing Maps locations service, see Locations API.
Phase 2: Retrieving route data
In the second phase, the TripOptimizerImpl::InternalOptimizeTripAsync method retrieves route data from Bing Maps. Route data is retrieved because the trip optimization algorithm requires the distance between each set of points in the graph. Recall that an edge connects two nodes in the graph and each node holds a location.
//
// Phase 2: Retrieve route information for each pair of locations.
//
// Report progress.
reporter.report("Retrieving routes (0% complete)...");
auto tasks = RetrieveRoutesAsync(params, cancellationToken, reporter);
// Move to the next phase after all current tasks complete.
return when_all(begin(tasks), end(tasks)).then(
[=](task<void> t) -> task<IMap<String^, IVector<String^>^>^>
{
// The PPL requires that all exceptions be caught and handled.
// If any task in the collection errors, ensure that all errors in the entire
// collection are observed at least one time.
try
{
// Calling task.get will cause any exception that occurred
// during the task to be thrown.
t.get();
}
catch (Exception^)
{
observe_all_exceptions<void>(begin(tasks), end(tasks));
// Rethrow the original exception.
throw;
}
catch (const task_canceled&)
{
observe_all_exceptions<void>(begin(tasks), end(tasks));
// Rethrow the original exception.
throw;
}
// Report progress.
reporter.report("Retrieving routes (100% complete)...");
// Record the elapsed HTTP time.
params->HttpTime = GetTickCount64() - params->HttpTime;
The TripOptimizerImpl::RetrieveRoutesAsync method follows a similar pattern as the TripOptimizerImpl::RetrieveLocationsAsync method to retrieve route data from Bing Maps.
// Retrieves distance information for each pair of locations from the Bing Maps route service.
vector<task<void>> TripOptimizerImpl::RetrieveRoutesAsync(
shared_ptr<OptimizeTripParams> params,
cancellation_token cancellationToken,
progress_reporter<String^> reporter)
{
// Holds the tasks that process the returned XML documents.
vector<task<void>> tasks;
// Implementation note:
// We assume that the route from A -> B is the same *distance* as
// the route from B -> A. Although this enables us to make fewer HTTP route requests,
// the final route might be slightly sub-optimal if the trip contains legs with
// one-way streets or the distance from A -> B differs from the distance from B -> A.
// (However, the end route will always contain the correct turn information.)
// Create HTTP requests for route information.
auto edges = params->Edges;
params->RequestsPending = static_cast<long>(edges.size()); // Used to report progress.
for (auto edge : edges)
{
// Create request URL.
LatLong pointA = edge->PointA->ResolvedLocation;
LatLong pointB = edge->PointB->ResolvedLocation;
wstringstream uri;
uri << L"http://dev.virtualearth.net/REST/v1/Routes/" << params->TravelMode
<< L"?wp.0=" << pointA.Latitude << L"," << pointA.Longitude
<< L"&wp.1=" << pointB.Latitude << L"," << pointB.Longitude
<< L"&optmz=" << params->Optimize
<< L"&o=xml"
<< L"&key=" << params->BingMapsKey;
// Create a parent task that downloads the XML document.
auto httpRequest = make_shared<HttpRequest>();
auto downloadTask = httpRequest->GetAsync(
ref new Uri(ref new String(uri.str().c_str())),
cancellationToken);
// Create a continuation task that fills route information after
// the download finishes.
tasks.push_back(downloadTask.then([=](task<wstring> previousTask)
{
(void)httpRequest;
// Get the result to force exceptions to be thrown.
wstring response = previousTask.get();
try
{
// Create and load the XML document from the response.
XmlDocument^ xmlDocument = ref new XmlDocument();
auto xml = ref new String(response.c_str() + 1); // Bypass BOM.
xmlDocument->LoadXml(xml);
// Fill in route information.
ProcessRoute(edge, xmlDocument);
}
catch (Exception^)
{
// An error occurred. Cancel any other active downloads.
m_cancellationTokenSource.cancel();
// Rethrow the exception.
throw;
}
// Report progress.
wstringstream progress;
progress << L"Retrieving routes ("
<< static_cast<long>(100.0 * (params->Edges.size() - params->RequestsPending) / params->Edges.size())
<< L"% complete)...";
reporter.report(ref new String(progress.str().c_str()));
InterlockedDecrement(¶ms->RequestsPending);
}));
}
return tasks;
}
The TripOptimizerImpl::ProcessRoute method follows a pattern that resembles the TripOptimizerImpl::ProcessLocation method to load XML data. The difference is that the TripOptimizerImpl::ProcessRoute method loads route information into an Edge object for a pair of travel locations.
//
// Update edges.
// Set travel distance.
edge->TravelDistance = travelDistance;
// Ensure that the distance is at least FLT_EPSILON.
// If the travel distance is very short, a value below FLT_EPSILON
// can result in a divide by zero error in the trip optimization algorithm.
if (edge->TravelDistance < FLT_EPSILON)
{
edge->TravelDistance = FLT_EPSILON;
}
// Set latitude and longitude of both points.
edge->PointA->ResolvedLocation = LatLong(lat0, lon0);
edge->PointB->ResolvedLocation = LatLong(lat1, lon1);
After the TripOptimizerImpl::RetrieveRoutesAsync method processes all route information, the TripOptimizerImpl::InternalOptimizeTripAsync method performs the final phase, which is to perform the route optimization.
For more information about the Bing Maps routes service, see Routes API.
Phase 3: Computing the optimized route
In the third phase, the TripOptimizerImpl::InternalOptimizeTripAsync method optimizes the route for shortest overall distance traveled. It calls the AntSystem::OptimizeRoute function, which uses nodes and edge information, together with other parameters, such as inputs to the ant colony optimization algorithm, to compute the optimized trip.
// Run the simulation.
vector<size_t> routeIndices = OptimizeRoute(
params->Nodes, params->Edges,
params->Alpha, params->Beta, params->Rho,
params->Iterations,
cancellationToken,
&progressCallback,
params->Parallel);
When the AntSystem::OptimizeRoute function returns, the TripOptimizerImpl::InternalOptimizeTripAsync method rotates the order to match the user input. In other words, it ensures that the user’s first entry is the first entry in the optimized route.
// Create the final route.
// The optimizer returns a route that has an arbitrary starting point.
// For example, the route might look like:
// A -> B -> C -> D -> E -> A
// If our starting point was D, we want the route to look like:
// D -> E -> A -> B -> C -> D
routeIndices.pop_back();
while (routeIndices.front() != 0)
{
routeIndices.push_back(routeIndices.front());
routeIndices.erase(begin(routeIndices));
}
routeIndices.push_back(routeIndices.front());
The TripOptimizerImpl::InternalOptimizeTripAsync method then creates parallel vectors that contain location data (latitude and longitude) and display names. These vectors are contained in a Platform::Collections::Map object. (Map is the C++ implementation for the Windows::Foundation::Collections::IMap<K, V> interface. Similarly, Platform::Collections::Vector is the C++ implementation for the Windows::Foundation::Collections::IVector<T> interface.) The main app uses the location data to display the map and the location names as part of the turn-by-turn directions.
//
// Prepare the return value.
//
// Parallel arrays that hold the optimized route locations and names.
IVector<String^>^ optimizedRoute; // {"47.620056,-122.349261", ...}
IVector<String^>^ optimizedRouteDisplayNames; // {"Space Needle, WA", ...}
optimizedRoute = ref new Vector<String^>();
optimizedRouteDisplayNames = ref new Vector<String^>();
// Fill the arrays.
size_t i = 0;
for (size_t index : routeIndices)
{
const auto node = params->Nodes[index];
String^ v;
// The location is the latitude and longitude of the waypoint.
// For example, "47.620056,-122.349261"
wstringstream location;
location << node->ResolvedLocation.Latitude << L',' << node->ResolvedLocation.Longitude;
v = ref new String(location.str().c_str());
optimizedRoute->InsertAt(static_cast<unsigned int>(i), v);
// The display name if the resolved name of the waypoint.
// For example, "Space Needle, WA"
v = ref new String(node->ResolvedName.c_str());
optimizedRouteDisplayNames->InsertAt(static_cast<unsigned int>(i), v);
++i;
}
// The return value.
auto finalRoute = ref new Map<String^, IVector<String^>^>();
finalRoute->Insert("locations", optimizedRoute);
finalRoute->Insert("displayNames", optimizedRouteDisplayNames);
// Compute the overall elapsed time.
params->TotalTime = GetTickCount64() - params->TotalTime;
// Report final progress.
// This message contains the overall elapsed time, the time spent performing
// HTTP requests, and the time it took to run the simulation.
wstringstream progress;
progress << L"Loading Map. Elapsed time: "
<< params->TotalTime << L"ms (total); "
<< params->HttpTime << L"ms (HTTP); "
<< params->SimulationTime << L"ms (simulation).";
reporter.report(ref new String(progress.str().c_str()));
return task_from_result<IMap<String^, IVector<String^>^>^>(finalRoute);
[]60dddf21-e817-4463-9417-a69b79e5e285#top
Defining HTTP functionality
The C++ component defines the HttpRequest class to process HTTP requests. This class uses the IXMLHTTPRequest2 interface to process HTTP requests. The IXMLHTTPRequest2 interface supports only asynchronous operations. To make it easier for the caller to consume these asynchronous operations, the HttpRequest::GetAsync method returns a concurrency::task<std::wstring> object. This task object holds the HTTP response as a string.
Note
IXMLHTTPRequest2 is one way to connect to the web service. To learn about other options for connecting to services in your app, see Connecting to web services.
Because IXMLHTTPRequest2 supports only asynchronous operations, you must provide an IXMLHTTPRequest2Callback object when you request data from an HTTP server. The HttpRequest.cpp file defines the HttpRequestStringCallback class, which inherits from this interface and implements its methods.
An important part of this implementation is the use of concurrency::task_completion_event. This class enables the HttpReader class to create a task that is set when another asynchronous task completes. This class is useful when you need to compose task objects together with asynchronous operations that complete through callbacks. When the download operation completes successfully, the HttpRequestStringCallback::OnResponseReceived method sets the completion event.
completionEvent.set(make_tuple<HRESULT, wstring>(move(hr), move(wstr)));
Accordingly, the HttpRequestStringCallback::OnError method sets the completion event when an error occurs. In this case, the error code and the empty string are the result of the task.
completionEvent.set(make_tuple<HRESULT, wstring>(move(hrError), wstring()));
The HttpRequest::GetAsync method calls HttpRequest::DownloadAsync. The HttpRequest::DownloadAsync method opens the asynchronous request and creates an HttpRequestStringCallback object. It then creates a task object that completes when the HttpRequestStringCallback object’s task completion event completes. This task object uses a continuation to release the HttpRequestStringCallback object after the task completion event completes.
// Start a download of the specified URI using the specified method. The returned task produces the
// HTTP response text. The status code and reason can be read with GetStatusCode() and GetReasonPhrase().
task<wstring> HttpRequest::DownloadAsync(PCWSTR httpMethod, PCWSTR uri, cancellation_token cancellationToken,
PCWSTR contentType, IStream* postStream, uint64 postStreamSizeToSend)
{
// Create an IXMLHTTPRequest2 object.
ComPtr<IXMLHTTPRequest2> xhr;
CheckHResult(CoCreateInstance(CLSID_XmlHttpRequest, nullptr, CLSCTX_INPROC, IID_PPV_ARGS(&xhr)));
// Create callback.
auto stringCallback = Make<HttpRequestStringCallback>(xhr.Get(), cancellationToken);
CheckHResult(stringCallback ? S_OK : E_OUTOFMEMORY);
auto completionTask = create_task(stringCallback->GetCompletionEvent());
// Create a request.
CheckHResult(xhr->Open(httpMethod, uri, stringCallback.Get(), nullptr, nullptr, nullptr, nullptr));
if (postStream != nullptr && contentType != nullptr)
{
CheckHResult(xhr->SetRequestHeader(L"Content-Type", contentType));
}
// Send the request.
CheckHResult(xhr->Send(postStream, postStreamSizeToSend));
// Return a task that completes when the HTTP operation completes.
// We pass the callback to the continuation because the lifetime of the
// callback must exceed the operation to ensure that cancellation
// works correctly.
return completionTask.then([this, stringCallback](tuple<HRESULT, wstring> resultTuple)
{
// If the GET operation failed, throw an Exception.
CheckHResult(std::get<0>(resultTuple));
statusCode = stringCallback->GetStatusCode();
reasonPhrase = stringCallback->GetReasonPhrase();
return std::get<1>(resultTuple);
});
}
For information about how the TripOptimizerImpl class uses the resulting XML data to compute the optimized route, see Component workflow in this document. For other examples of how to use IXMLHTTPRequest2, see and Walkthrough: Connecting Using Tasks and XML HTTP Requests.CC7AED53-B2C5-4D83-B85D-CFF2F5BA7B35
[]60dddf21-e817-4463-9417-a69b79e5e285#top
Computing the optimal route
The core algorithm that performs the route computation is defined in AntSystem.h and AntSystem.cpp. The route computation resembles the traveling salesman problem. The goal of the traveling salesman problem is to take a collection of locations and the distance between each pair of locations and compute the shortest route that visits each location one time only. The traveling salesman problem is traditionally difficult to solve because it requires every possible route to be computed in order to find the optimal route. The ant colony optimization algorithm is a metaheuristic approach to solving this class of problems. It models the behavior of ants to quickly find an optimized route. Although the routes produced by this algorithm are not guaranteed to be the shortest for a given set of locations, it often does find the shortest route or is sufficiently short for travel purposes.
The AntSystem.h and AntSystem.cpp files define the AntSystem namespace. This namespace contains no dependencies on the Windows Runtime and therefore does not use C++/CX. AntSystem.h defines the LatLong, Node, and Edge structures. It also defines the OptimizeRoute function.
The LatLong structure represents the latitude and longitude of a point on a map.
// Represents the latitude and longitude of a single point on a map.
struct LatLong
{
explicit LatLong(double latitude, double longitude)
: Latitude(latitude)
, Longitude(longitude)
{
}
// The coordinates of the location.
double Latitude;
double Longitude;
};
The Node structure represents a node in a graph. It holds the name, latitude, and longitude of a location. It also holds any alternative names that came from the Bing Maps service.
// Represents a node in a graph.
struct Node
{
explicit Node(const std::wstring& inputName)
: InputName(inputName)
, ResolvedLocation(0.0, 0.0)
{
}
// The name of the location as provided by the user.
std::wstring InputName;
// The resolved latitude and longitude of the location as provided by the
// Bing Maps location service.
LatLong ResolvedLocation;
// The resolved name of the location as provided by the
// Bing Maps location service.
std::wstring ResolvedName;
//
// Parallel arrays of string names and latitude, longitude pairs that represent
// all possible resolved locations for the current input name.
// For example, if the input name is "Redmond", the Names array might contain
// "Redmond, WA", "Redmond, OR", "Redmond, UT", and "Redmond, Australia".
// The Locations array would contain the corresponding latitude and longitude
// for each location.
std::vector<std::wstring> Names;
std::vector<LatLong> Locations;
};
The Edge structure connects two nodes and holds the travel distance between them. It also holds data that's used by the ant colony optimization algorithm.
// Represents an edge in a graph of Node objects.
// An Edge object connects two nodes and holds the travel distance between
// those nodes. An Edge object also holds the amount of pheromone that
// exists on that edge.
struct Edge
{
explicit Edge(std::shared_ptr<Node> pointA, std::shared_ptr<Node> pointB)
: PointA(pointA)
, PointB(pointB)
, Pheromone(0.0)
, TravelDistance(-1.0)
{
}
// The start node.
std::shared_ptr<Node> PointA;
// The end node.
std::shared_ptr<Node> PointB;
// The amount of pheromone on the edge.
double Pheromone;
// The distance from the start node to the end node.
double TravelDistance;
};
The C++ component creates a Node object for each location in the trip and an Edge object for every pair of locations. After it collects all required information from the Bing Maps web services, it calls OptimizeRoute to compute the optimal route.
// Optimizes the route among the given nodes for the shortest travel distance.
// This method returns indicies to the provided Node collection.
std::vector<size_t> OptimizeRoute(
std::vector<std::shared_ptr<Node>>& nodes,
std::vector<std::shared_ptr<Edge>>& edges,
double alpha,
double beta,
double rho,
unsigned int iterations,
Concurrency::cancellation_token cancellationToken,
std::function<void(unsigned int)>* progressCallback = nullptr,
bool parallel = true);
For brevity, this documentation doesn't describe the ant colony optimization algorithm in detail. For details, see AntSystem.cpp in the source code.
However, one important aspect of the implementation of the algorithm is the use of concurrency. The ant colony optimization algorithm performs several iterations of three basic steps: allow for each ant to tour the graph, evaporate pheromone, and then allow for each ant to trace its steps back to the starting point. The first step, allowing for each ant to tour the graph, can be performed in parallel because each ant acts independently. This step contains no shared data or dependent computations.
// Perform the simulation several times.
auto startTime = GetTickCount64();
for (unsigned int i = 0; i < iterations; ++i)
{
// Occasionally check for cancellation.
auto time = GetTickCount64();
if (time - startTime > 100)
{
if (cancellationToken.is_canceled())
{
// Return the empty collection.
return vector<size_t>();
}
startTime = time;
}
// Send progress.
if (progressCallback != nullptr)
{
(*progressCallback)(i);
}
//
// Allow each ant to perform a tour of the graph.
// Note that this operation can be performed in parallel because each ant acts independently.
// This step contains no shared data or dependent computations.
if (parallel)
{
parallel_for_each(begin(ants), end(ants), [&](Ant& blitz)
{
blitz.Explore();
});
}
else
{
for_each(begin(ants), end(ants), [&](Ant& blitz)
{
blitz.Explore();
});
}
//
// Evaporate pheromone.
for_each(begin(edges), end(edges), [rho](shared_ptr<Edge> edge)
{
edge->Pheromone *= (1.0 - rho);
});
//
// Allow each ant to backtrack through the graph and drop pheromone on
// each edge.
//
// Note that this operation is NOT performed in parallel because
// the ants update the pherone value of each edge.
// Because the backtrack operation is not relatively lengthy, the
// overhead that is required to synchronize access to the edges would
// likely outweigh any benefits of parallel processing.
for_each(begin(ants), end(ants), [&](Ant& blitz)
{
blitz.Backtrack();
});
}
For more information about parallel algorithms such as parallel_for_each, see Parallel Algorithms.
Note
The use of the parallel flag is not a required part of the implementation. It's provided as an option in the UI so that you can more easily experiment with parallel—versus serial—computation.
[]60dddf21-e817-4463-9417-a69b79e5e285#top
Handling cancellation
The IAsyncAction, IAsyncActionWithProgress<TProgress>, IAsyncOperation<TResult>, and IAsyncOperationWithProgress<TResult, TProgress> interfaces each provide a Cancel method that enables you to cancel the asynchronous operation. In C++, these Cancel methods cancel a cancellation token that is associated with the asynchronous operation. You can connect task cancellation with the Windows Runtime Cancel methods in two ways. First, you can define the work function that you pass to create_async to take a concurrency::cancellation_token object. When the Cancel method is called, this cancellation token is cancelled and the normal cancellation rules apply. If you do not provide a cancellation_token object, the underlying task object defines one implicitly. Define a cancellation_token object when you need to cooperatively respond to cancellation in your work function. For more information about this cancellation mechanism, see Creating Asynchronous Operations in C++ for Windows Store Apps.
Cancellation occurs when the user chooses the Cancel button in the JavaScript app or an unrecoverable error occurs. Enabling the user to cancel is one reason why it's important to keep the UI responsive during the optimization task. Cancellation does not occur immediately. The C++ component uses concurrency::cancellation_token_source and concurrency::cancellation_token to signal cancellation and to occasionally check for cancellation. The C++ component implements cancellation on as much of a coarse-grained level as possible, but still strives to enable cancellation to occur in a timely manner. From a performance standpoint, the app would not benefit if it checked for cancellation too frequently. In fact, performance can suffer if the time spent checking for cancellation is greater than the time spent doing work.
The C++ component checks for cancelation in two different ways. First, the continuation task that occurs after each optimization phase calls concurrency::task::get to test for cancellation. The task::get method throws any exception that occurred during the task, including task_canceled if cancellation occurred. (In the case of when_all, the runtime chooses one of the exceptions if multiple tasks threw.) Because you must observe all task exceptions that occur, we define the observe_all_exceptions function to observe all exceptions that occurred in the tasks that are provided to the when_all algorithm. The following example shows the check for cancellation after locations are retrieved from Bing Maps but before routes are retrieved.
//
// Phase 2: Retrieve route information for each pair of locations.
//
// Report progress.
reporter.report("Retrieving routes (0% complete)...");
auto tasks = RetrieveRoutesAsync(params, cancellationToken, reporter);
// Move to the next phase after all current tasks complete.
return when_all(begin(tasks), end(tasks)).then(
[=](task<void> t) -> task<IMap<String^, IVector<String^>^>^>
{
// The PPL requires that all exceptions be caught and handled.
// If any task in the collection errors, ensure that all errors in the entire
// collection are observed at least one time.
try
{
// Calling task.get will cause any exception that occurred
// during the task to be thrown.
t.get();
}
catch (Exception^)
{
observe_all_exceptions<void>(begin(tasks), end(tasks));
// Rethrow the original exception.
throw;
}
catch (const task_canceled&)
{
observe_all_exceptions<void>(begin(tasks), end(tasks));
// Rethrow the original exception.
throw;
}
// Report progress.
reporter.report("Retrieving routes (100% complete)...");
// Record the elapsed HTTP time.
params->HttpTime = GetTickCount64() - params->HttpTime;
After the call to observe_all_exceptions, we rethrow the original exception so that it can be handled by the code that depends on that task.
The following shows the observe_all_exceptions. It iterates over each task object in the provided collection and uses the task::get method to test for errors. Because we plan to later rethrow one of the exceptions, we use empty catch blocks to indicate that the exception has been observed and handled.
// Observes all exceptions that occurred in all tasks in the given range.
template<class T, class InIt>
void observe_all_exceptions(InIt first, InIt last)
{
for_each (first, last, [](task<T> t)
{
t.then([](task<T> previousTask)
{
try
{
previousTask.get();
}
catch (Exception^)
{
// Swallow the exception.
}
catch (const exception&)
{
// Swallow the exception.
}
});
});
}
The second way that the component checks for cancellation is by calling the concurrency::cancellation_token::is_canceled method. The trip optimization algorithm (the AntSystem::OptimizeRoute function) checks for cancellation in this way every 100 milliseconds.
// Perform the simulation several times.
auto startTime = GetTickCount64();
for (unsigned int i = 0; i < iterations; ++i)
{
// Occasionally check for cancellation.
auto time = GetTickCount64();
if (time - startTime > 100)
{
if (cancellationToken.is_canceled())
{
// Return the empty collection.
return vector<size_t>();
}
startTime = time;
}
Note
We could have used the first technique, which is to call is_task_cancellation_requested and cancel_current_task, instead of calling cancellation_token::is_canceled. However, cancel_current_task must be called from a task object. Because in theory you can use this implementation to call the AntSystem::OptimizeRoute function from a task or from another part of the code, we use cancellation tokens directly to be flexible. If this function were to be called from code that does not use tasks, you could pass concurrency::cancellation_token::none for the cancellationToken parameter. The none token can never be canceled.
The section Defining HTTP functionality describes how the HttpRequestStringCallback class uses task_completion_event to compose asynchronous operations that complete through callbacks together with task objects. Similarly, to support cancellation, the HttpRequestStringCallback class uses the concurrency::cancellation_token::register_callback method to register a callback function that is called when the cancellation token is canceled. This technique is useful because the IXMLHTTPRequest2 interface performs asynchronous work that is outside our control. When the cancellation token is cancelled, the callback function aborts the HTTP request and sets the task completion event.
HttpRequestStringCallback(IXMLHTTPRequest2* r, cancellation_token ct) :
request(r), cancellationToken(ct)
{
// Register a callback function that aborts the HTTP operation when
// the cancellation token is canceled.
if (cancellationToken != cancellation_token::none())
{
registrationToken = cancellationToken.register_callback([this]()
{
if (request != nullptr)
{
request->Abort();
}
});
}
}
The cancellation_token::register_callback returns a concurrency::cancellation_token_registration object that identifies the callback registration. The destructor of the HttpRequest class uses this registration object to unregister the callback function. We recommend that you always unregister your callback when you no longer need it to ensure that all objects are valid when a callback function is called.
~HttpRequestStringCallback()
{
// Unregister the callback.
if (cancellationToken != cancellation_token::none())
{
cancellationToken.deregister_callback(registrationToken);
}
}
In the case of an unrecoverable error, any remaining tasks are canceled. For example, if an XML document cannot be processed, the overall operation is canceled and the exception is rethrown.
try
{
// Create and load the XML document from the response.
XmlDocument^ xmlDocument = ref new XmlDocument();
auto xml = ref new String(response.c_str() + 1); // Bypass BOM.
xmlDocument->LoadXml(xml);
// Fill in location information.
ProcessLocation(node, xmlDocument, params->UnresolvedLocations);
}
catch (Exception^)
{
// An error occurred. Cancel any active operations.
m_cancellationTokenSource.cancel();
// Rethrow the exception.
throw;
}
The TripOptimizerImpl class defines a concurrency::cancellation_token_source object because cancellation is initiated through this class. To enable both the Cancel button and internal code to cancel the tasks, the TripOptimizerImpl class calls the concurrency::cancellation_token_source::create_linked_source method. This linked cancellation token source enables both the JavaScript app and the TripOptimizerImpl class to cancel the same cancellation token, but from different cancellation_token_source objects.
// Perform the operation asynchronously.
return create_async([this, params](progress_reporter<String^> reporter,
cancellation_token cancellationToken) -> task<IMap<String^, IVector<String^>^>^>
{
// Create a linked source for cancellation.
// This enables both the caller (through the returned
// IAsyncOperationWithProgress object) and this class to set
// the same cancellation token.
m_cancellationTokenSource =
cancellation_token_source::create_linked_source(cancellationToken);
For more information about how cancellation works in the Parallel Patterns Library, see Cancellation in the PPL.
[]60dddf21-e817-4463-9417-a69b79e5e285#top
Migration from ActiveX
For information about how we migrated from the ActiveX version of Bing Maps Trip Optimizer to a Windows 8.x Store app, see Migrating existing code in the Bing Maps Trip Optimizer sample.
[]60dddf21-e817-4463-9417-a69b79e5e285#top
Next steps
For information about how the C++ Windows Runtime component interoperates with the JavaScript component, see Interoperating between JavaScript and C++ in the Bing Maps Trip Optimizer sample.
See Also
Developing Bing Maps Trip Optimizer, a Windows Store app in JavaScript and C++
Interoperating between JavaScript and C++ in the Bing Maps Trip Optimizer sample
Using JavaScript in the Bing Maps Trip Optimizer sample